Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/config/config_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ mod test {
let base_rules = vec![
(RuleEnum::EslintCurly(EslintCurly::default()), AllowWarnDeny::Deny),
(
RuleEnum::TypescriptNoMisusedPromises(TypescriptNoMisusedPromises),
RuleEnum::TypescriptNoMisusedPromises(TypescriptNoMisusedPromises::default()),
AllowWarnDeny::Deny,
),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
use oxc_macros::declare_oxc_lint;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::rule::Rule;
use crate::rule::{DefaultRuleConfig, Rule};

#[derive(Debug, Default, Clone)]
pub struct NoConfusingVoidExpression;
pub struct NoConfusingVoidExpression(Box<NoConfusingVoidExpressionConfig>);

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct NoConfusingVoidExpressionConfig {
/// Whether to ignore arrow function shorthand that returns void.
/// When true, allows expressions like `() => someVoidFunction()`.
pub ignore_arrow_shorthand: bool,
/// Whether to ignore expressions using the void operator.
/// When true, allows `void someExpression`.
pub ignore_void_operator: bool,
/// Whether to ignore calling functions that are declared to return void.
/// When true, allows expressions like `x = voidReturningFunction()`.
pub ignore_void_returning_functions: bool,
}

declare_oxc_lint!(
/// ### What it does
Expand Down Expand Up @@ -53,6 +69,19 @@ declare_oxc_lint!(
typescript,
pedantic,
pending,
config = NoConfusingVoidExpressionConfig,
);

impl Rule for NoConfusingVoidExpression {}
impl Rule for NoConfusingVoidExpression {
fn from_configuration(value: serde_json::Value) -> Self {
Self(Box::new(
serde_json::from_value::<DefaultRuleConfig<NoConfusingVoidExpressionConfig>>(value)
.unwrap_or_default()
.into_inner(),
))
}

fn to_configuration(&self) -> Option<Result<serde_json::Value, serde_json::Error>> {
Some(serde_json::to_value(&*self.0))
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
use oxc_macros::declare_oxc_lint;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::rule::Rule;
use crate::rule::{DefaultRuleConfig, Rule};

#[derive(Debug, Default, Clone)]
pub struct NoDuplicateTypeConstituents;
pub struct NoDuplicateTypeConstituents(Box<NoDuplicateTypeConstituentsConfig>);

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct NoDuplicateTypeConstituentsConfig {
/// Whether to ignore duplicate types in intersection types.
/// When true, allows `type T = A & A`.
pub ignore_intersections: bool,
/// Whether to ignore duplicate types in union types.
/// When true, allows `type T = A | A`.
pub ignore_unions: bool,
}

declare_oxc_lint!(
/// ### What it does
Expand Down Expand Up @@ -51,6 +64,19 @@ declare_oxc_lint!(
typescript,
correctness,
pending,
config = NoDuplicateTypeConstituentsConfig,
);

impl Rule for NoDuplicateTypeConstituents {}
impl Rule for NoDuplicateTypeConstituents {
fn from_configuration(value: serde_json::Value) -> Self {
Self(Box::new(
serde_json::from_value::<DefaultRuleConfig<NoDuplicateTypeConstituentsConfig>>(value)
.unwrap_or_default()
.into_inner(),
))
}

fn to_configuration(&self) -> Option<Result<serde_json::Value, serde_json::Error>> {
Some(serde_json::to_value(&*self.0))
}
}
110 changes: 4 additions & 106 deletions crates/oxc_linter/src/rules/typescript/no_floating_promises.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use oxc_macros::declare_oxc_lint;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::rule::{DefaultRuleConfig, Rule};
use crate::{
rule::{DefaultRuleConfig, Rule},
utils::TypeOrValueSpecifier,
};

#[derive(Debug, Default, Clone)]
pub struct NoFloatingPromises(Box<NoFloatingPromisesConfig>);
Expand All @@ -23,111 +26,6 @@ pub struct NoFloatingPromisesConfig {
pub ignore_void: bool,
}

/// Type or value specifier for matching specific declarations
///
/// Supports four types of specifiers:
///
/// 1. **String specifier** (deprecated): Universal match by name
/// ```json
/// "Promise"
/// ```
///
/// 2. **File specifier**: Match types/values declared in local files
/// ```json
/// { "from": "file", "name": "MyType" }
/// { "from": "file", "name": ["Type1", "Type2"] }
/// { "from": "file", "name": "MyType", "path": "./types.ts" }
/// ```
///
/// 3. **Lib specifier**: Match TypeScript built-in lib types
/// ```json
/// { "from": "lib", "name": "Promise" }
/// { "from": "lib", "name": ["Promise", "PromiseLike"] }
/// ```
///
/// 4. **Package specifier**: Match types/values from npm packages
/// ```json
/// { "from": "package", "name": "Observable", "package": "rxjs" }
/// { "from": "package", "name": ["Observable", "Subject"], "package": "rxjs" }
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum TypeOrValueSpecifier {
/// Universal string specifier - matches all types and values with this name regardless of declaration source.
/// Not recommended - will be removed in a future major version.
String(String),
/// Describes specific types or values declared in local files.
File(FileSpecifier),
/// Describes specific types or values declared in TypeScript's built-in lib.*.d.ts types.
Lib(LibSpecifier),
/// Describes specific types or values imported from packages.
Package(PackageSpecifier),
}

/// Describes specific types or values declared in local files.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct FileSpecifier {
/// Must be "file"
from: FileFrom,
/// The name(s) of the type or value to match
name: NameSpecifier,
/// Optional file path to specify where the types or values must be declared.
/// If omitted, all files will be matched.
#[serde(skip_serializing_if = "Option::is_none")]
path: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
enum FileFrom {
File,
}

/// Describes specific types or values declared in TypeScript's built-in lib.*.d.ts types.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct LibSpecifier {
/// Must be "lib"
from: LibFrom,
/// The name(s) of the lib type or value to match
name: NameSpecifier,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
enum LibFrom {
Lib,
}

/// Describes specific types or values imported from packages.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct PackageSpecifier {
/// Must be "package"
from: PackageFrom,
/// The name(s) of the type or value to match
name: NameSpecifier,
/// The package name to match
package: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
enum PackageFrom {
Package,
}

/// Name specifier that can be a single string or array of strings
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum NameSpecifier {
/// Single name
Single(String),
/// Multiple names
Multiple(Vec<String>),
}

impl Default for NoFloatingPromisesConfig {
fn default() -> Self {
Self {
Expand Down
99 changes: 96 additions & 3 deletions crates/oxc_linter/src/rules/typescript/no_misused_promises.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,89 @@
use oxc_macros::declare_oxc_lint;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::rule::Rule;
use crate::{
rule::{DefaultRuleConfig, Rule},
utils::default_true,
};

fn default_checks_void_return() -> ChecksVoidReturn {
ChecksVoidReturn::Boolean(true)
}

#[derive(Debug, Default, Clone)]
pub struct NoMisusedPromises;
pub struct NoMisusedPromises(Box<NoMisusedPromisesConfig>);

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default)]
#[expect(clippy::struct_field_names)]
pub struct NoMisusedPromisesConfig {
/// Whether to check if Promises are used in conditionals.
/// When true, disallows using Promises in conditions where a boolean is expected.
#[serde(default = "default_true")]
pub checks_conditionals: bool,
/// Whether to check if Promises are used in spread syntax.
/// When true, disallows spreading Promise values.
#[serde(default = "default_true")]
pub checks_spreads: bool,
/// Configuration for checking if Promises are returned in contexts expecting void.
/// Can be a boolean to enable/disable all checks, or an object for granular control.
#[serde(default = "default_checks_void_return")]
pub checks_void_return: ChecksVoidReturn,
}

impl Default for NoMisusedPromisesConfig {
fn default() -> Self {
Self {
checks_conditionals: true,
checks_spreads: true,
checks_void_return: ChecksVoidReturn::Boolean(true),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum ChecksVoidReturn {
Boolean(bool),
Options(ChecksVoidReturnOptions),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default)]
pub struct ChecksVoidReturnOptions {
/// Whether to check Promise-returning functions passed as arguments to void-returning functions.
#[serde(default = "default_true")]
pub arguments: bool,
/// Whether to check Promise-returning functions in JSX attributes expecting void.
#[serde(default = "default_true")]
pub attributes: bool,
/// Whether to check Promise-returning methods that override void-returning inherited methods.
#[serde(default = "default_true")]
pub inherited_methods: bool,
/// Whether to check Promise-returning functions assigned to object properties expecting void.
#[serde(default = "default_true")]
pub properties: bool,
/// Whether to check Promise values returned from void-returning functions.
#[serde(default = "default_true")]
pub returns: bool,
/// Whether to check Promise-returning functions assigned to variables typed as void-returning.
#[serde(default = "default_true")]
pub variables: bool,
}

impl Default for ChecksVoidReturnOptions {
fn default() -> Self {
Self {
arguments: true,
attributes: true,
inherited_methods: true,
properties: true,
returns: true,
variables: true,
}
}
}

declare_oxc_lint!(
/// ### What it does
Expand Down Expand Up @@ -55,6 +135,19 @@ declare_oxc_lint!(
typescript,
pedantic,
pending,
config = NoMisusedPromisesConfig,
);

impl Rule for NoMisusedPromises {}
impl Rule for NoMisusedPromises {
fn from_configuration(value: serde_json::Value) -> Self {
Self(Box::new(
serde_json::from_value::<DefaultRuleConfig<NoMisusedPromisesConfig>>(value)
.unwrap_or_default()
.into_inner(),
))
}

fn to_configuration(&self) -> Option<Result<serde_json::Value, serde_json::Error>> {
Some(serde_json::to_value(&*self.0))
}
}
Loading
Loading