diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index c8b1d36ffb26b..76645bc74d259 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -1769,6 +1769,12 @@ impl RuleRunner for crate::rules::typescript::no_unsafe_unary_minus::NoUnsafeUna const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown; } +impl RuleRunner for crate::rules::typescript::no_use_before_define::NoUseBeforeDefine { + const NODE_TYPES: Option<&AstTypesBitset> = + Some(&AstTypesBitset::from_types(&[AstType::IdentifierReference])); + const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; +} + impl RuleRunner for crate::rules::typescript::no_useless_empty_export::NoUselessEmptyExport { const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[AstType::ExportNamedDeclaration])); diff --git a/crates/oxc_linter/src/generated/rules_enum.rs b/crates/oxc_linter/src/generated/rules_enum.rs index 782fd24a48046..3460574a0be97 100644 --- a/crates/oxc_linter/src/generated/rules_enum.rs +++ b/crates/oxc_linter/src/generated/rules_enum.rs @@ -497,6 +497,7 @@ pub use crate::rules::typescript::no_unsafe_member_access::NoUnsafeMemberAccess pub use crate::rules::typescript::no_unsafe_return::NoUnsafeReturn as TypescriptNoUnsafeReturn; pub use crate::rules::typescript::no_unsafe_type_assertion::NoUnsafeTypeAssertion as TypescriptNoUnsafeTypeAssertion; pub use crate::rules::typescript::no_unsafe_unary_minus::NoUnsafeUnaryMinus as TypescriptNoUnsafeUnaryMinus; +pub use crate::rules::typescript::no_use_before_define::NoUseBeforeDefine as TypescriptNoUseBeforeDefine; pub use crate::rules::typescript::no_useless_empty_export::NoUselessEmptyExport as TypescriptNoUselessEmptyExport; pub use crate::rules::typescript::no_var_requires::NoVarRequires as TypescriptNoVarRequires; pub use crate::rules::typescript::no_wrapper_object_types::NoWrapperObjectTypes as TypescriptNoWrapperObjectTypes; @@ -955,6 +956,7 @@ pub enum RuleEnum { TypescriptNoUnsafeReturn(TypescriptNoUnsafeReturn), TypescriptNoUnsafeTypeAssertion(TypescriptNoUnsafeTypeAssertion), TypescriptNoUnsafeUnaryMinus(TypescriptNoUnsafeUnaryMinus), + TypescriptNoUseBeforeDefine(TypescriptNoUseBeforeDefine), TypescriptNoUselessEmptyExport(TypescriptNoUselessEmptyExport), TypescriptNoVarRequires(TypescriptNoVarRequires), TypescriptNoWrapperObjectTypes(TypescriptNoWrapperObjectTypes), @@ -1658,7 +1660,8 @@ const TYPESCRIPT_NO_UNSAFE_MEMBER_ACCESS_ID: usize = TYPESCRIPT_NO_UNSAFE_FUNCTI const TYPESCRIPT_NO_UNSAFE_RETURN_ID: usize = TYPESCRIPT_NO_UNSAFE_MEMBER_ACCESS_ID + 1usize; const TYPESCRIPT_NO_UNSAFE_TYPE_ASSERTION_ID: usize = TYPESCRIPT_NO_UNSAFE_RETURN_ID + 1usize; const TYPESCRIPT_NO_UNSAFE_UNARY_MINUS_ID: usize = TYPESCRIPT_NO_UNSAFE_TYPE_ASSERTION_ID + 1usize; -const TYPESCRIPT_NO_USELESS_EMPTY_EXPORT_ID: usize = TYPESCRIPT_NO_UNSAFE_UNARY_MINUS_ID + 1usize; +const TYPESCRIPT_NO_USE_BEFORE_DEFINE_ID: usize = TYPESCRIPT_NO_UNSAFE_UNARY_MINUS_ID + 1usize; +const TYPESCRIPT_NO_USELESS_EMPTY_EXPORT_ID: usize = TYPESCRIPT_NO_USE_BEFORE_DEFINE_ID + 1usize; const TYPESCRIPT_NO_VAR_REQUIRES_ID: usize = TYPESCRIPT_NO_USELESS_EMPTY_EXPORT_ID + 1usize; const TYPESCRIPT_NO_WRAPPER_OBJECT_TYPES_ID: usize = TYPESCRIPT_NO_VAR_REQUIRES_ID + 1usize; const TYPESCRIPT_NON_NULLABLE_TYPE_ASSERTION_STYLE_ID: usize = @@ -2431,6 +2434,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(_) => TYPESCRIPT_NO_UNSAFE_RETURN_ID, Self::TypescriptNoUnsafeTypeAssertion(_) => TYPESCRIPT_NO_UNSAFE_TYPE_ASSERTION_ID, Self::TypescriptNoUnsafeUnaryMinus(_) => TYPESCRIPT_NO_UNSAFE_UNARY_MINUS_ID, + Self::TypescriptNoUseBeforeDefine(_) => TYPESCRIPT_NO_USE_BEFORE_DEFINE_ID, Self::TypescriptNoUselessEmptyExport(_) => TYPESCRIPT_NO_USELESS_EMPTY_EXPORT_ID, Self::TypescriptNoVarRequires(_) => TYPESCRIPT_NO_VAR_REQUIRES_ID, Self::TypescriptNoWrapperObjectTypes(_) => TYPESCRIPT_NO_WRAPPER_OBJECT_TYPES_ID, @@ -3204,6 +3208,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(_) => TypescriptNoUnsafeReturn::NAME, Self::TypescriptNoUnsafeTypeAssertion(_) => TypescriptNoUnsafeTypeAssertion::NAME, Self::TypescriptNoUnsafeUnaryMinus(_) => TypescriptNoUnsafeUnaryMinus::NAME, + Self::TypescriptNoUseBeforeDefine(_) => TypescriptNoUseBeforeDefine::NAME, Self::TypescriptNoUselessEmptyExport(_) => TypescriptNoUselessEmptyExport::NAME, Self::TypescriptNoVarRequires(_) => TypescriptNoVarRequires::NAME, Self::TypescriptNoWrapperObjectTypes(_) => TypescriptNoWrapperObjectTypes::NAME, @@ -3979,6 +3984,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(_) => TypescriptNoUnsafeReturn::CATEGORY, Self::TypescriptNoUnsafeTypeAssertion(_) => TypescriptNoUnsafeTypeAssertion::CATEGORY, Self::TypescriptNoUnsafeUnaryMinus(_) => TypescriptNoUnsafeUnaryMinus::CATEGORY, + Self::TypescriptNoUseBeforeDefine(_) => TypescriptNoUseBeforeDefine::CATEGORY, Self::TypescriptNoUselessEmptyExport(_) => TypescriptNoUselessEmptyExport::CATEGORY, Self::TypescriptNoVarRequires(_) => TypescriptNoVarRequires::CATEGORY, Self::TypescriptNoWrapperObjectTypes(_) => TypescriptNoWrapperObjectTypes::CATEGORY, @@ -4777,6 +4783,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(_) => TypescriptNoUnsafeReturn::FIX, Self::TypescriptNoUnsafeTypeAssertion(_) => TypescriptNoUnsafeTypeAssertion::FIX, Self::TypescriptNoUnsafeUnaryMinus(_) => TypescriptNoUnsafeUnaryMinus::FIX, + Self::TypescriptNoUseBeforeDefine(_) => TypescriptNoUseBeforeDefine::FIX, Self::TypescriptNoUselessEmptyExport(_) => TypescriptNoUselessEmptyExport::FIX, Self::TypescriptNoVarRequires(_) => TypescriptNoVarRequires::FIX, Self::TypescriptNoWrapperObjectTypes(_) => TypescriptNoWrapperObjectTypes::FIX, @@ -5591,6 +5598,7 @@ impl RuleEnum { TypescriptNoUnsafeTypeAssertion::documentation() } Self::TypescriptNoUnsafeUnaryMinus(_) => TypescriptNoUnsafeUnaryMinus::documentation(), + Self::TypescriptNoUseBeforeDefine(_) => TypescriptNoUseBeforeDefine::documentation(), Self::TypescriptNoUselessEmptyExport(_) => { TypescriptNoUselessEmptyExport::documentation() } @@ -6915,6 +6923,10 @@ impl RuleEnum { TypescriptNoUnsafeUnaryMinus::config_schema(generator) .or_else(|| TypescriptNoUnsafeUnaryMinus::schema(generator)) } + Self::TypescriptNoUseBeforeDefine(_) => { + TypescriptNoUseBeforeDefine::config_schema(generator) + .or_else(|| TypescriptNoUseBeforeDefine::schema(generator)) + } Self::TypescriptNoUselessEmptyExport(_) => { TypescriptNoUselessEmptyExport::config_schema(generator) .or_else(|| TypescriptNoUselessEmptyExport::schema(generator)) @@ -8357,6 +8369,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(_) => "typescript", Self::TypescriptNoUnsafeTypeAssertion(_) => "typescript", Self::TypescriptNoUnsafeUnaryMinus(_) => "typescript", + Self::TypescriptNoUseBeforeDefine(_) => "typescript", Self::TypescriptNoUselessEmptyExport(_) => "typescript", Self::TypescriptNoVarRequires(_) => "typescript", Self::TypescriptNoWrapperObjectTypes(_) => "typescript", @@ -9609,6 +9622,9 @@ impl RuleEnum { Self::TypescriptNoUnsafeUnaryMinus(_) => Ok(Self::TypescriptNoUnsafeUnaryMinus( TypescriptNoUnsafeUnaryMinus::from_configuration(value)?, )), + Self::TypescriptNoUseBeforeDefine(_) => Ok(Self::TypescriptNoUseBeforeDefine( + TypescriptNoUseBeforeDefine::from_configuration(value)?, + )), Self::TypescriptNoUselessEmptyExport(_) => Ok(Self::TypescriptNoUselessEmptyExport( TypescriptNoUselessEmptyExport::from_configuration(value)?, )), @@ -11194,6 +11210,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(rule) => rule.to_configuration(), Self::TypescriptNoUnsafeTypeAssertion(rule) => rule.to_configuration(), Self::TypescriptNoUnsafeUnaryMinus(rule) => rule.to_configuration(), + Self::TypescriptNoUseBeforeDefine(rule) => rule.to_configuration(), Self::TypescriptNoUselessEmptyExport(rule) => rule.to_configuration(), Self::TypescriptNoVarRequires(rule) => rule.to_configuration(), Self::TypescriptNoWrapperObjectTypes(rule) => rule.to_configuration(), @@ -11873,6 +11890,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(rule) => rule.run(node, ctx), Self::TypescriptNoUnsafeTypeAssertion(rule) => rule.run(node, ctx), Self::TypescriptNoUnsafeUnaryMinus(rule) => rule.run(node, ctx), + Self::TypescriptNoUseBeforeDefine(rule) => rule.run(node, ctx), Self::TypescriptNoUselessEmptyExport(rule) => rule.run(node, ctx), Self::TypescriptNoVarRequires(rule) => rule.run(node, ctx), Self::TypescriptNoWrapperObjectTypes(rule) => rule.run(node, ctx), @@ -12550,6 +12568,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(rule) => rule.run_once(ctx), Self::TypescriptNoUnsafeTypeAssertion(rule) => rule.run_once(ctx), Self::TypescriptNoUnsafeUnaryMinus(rule) => rule.run_once(ctx), + Self::TypescriptNoUseBeforeDefine(rule) => rule.run_once(ctx), Self::TypescriptNoUselessEmptyExport(rule) => rule.run_once(ctx), Self::TypescriptNoVarRequires(rule) => rule.run_once(ctx), Self::TypescriptNoWrapperObjectTypes(rule) => rule.run_once(ctx), @@ -13273,6 +13292,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(rule) => rule.run_on_jest_node(jest_node, ctx), Self::TypescriptNoUnsafeTypeAssertion(rule) => rule.run_on_jest_node(jest_node, ctx), Self::TypescriptNoUnsafeUnaryMinus(rule) => rule.run_on_jest_node(jest_node, ctx), + Self::TypescriptNoUseBeforeDefine(rule) => rule.run_on_jest_node(jest_node, ctx), Self::TypescriptNoUselessEmptyExport(rule) => rule.run_on_jest_node(jest_node, ctx), Self::TypescriptNoVarRequires(rule) => rule.run_on_jest_node(jest_node, ctx), Self::TypescriptNoWrapperObjectTypes(rule) => rule.run_on_jest_node(jest_node, ctx), @@ -13994,6 +14014,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(rule) => rule.should_run(ctx), Self::TypescriptNoUnsafeTypeAssertion(rule) => rule.should_run(ctx), Self::TypescriptNoUnsafeUnaryMinus(rule) => rule.should_run(ctx), + Self::TypescriptNoUseBeforeDefine(rule) => rule.should_run(ctx), Self::TypescriptNoUselessEmptyExport(rule) => rule.should_run(ctx), Self::TypescriptNoVarRequires(rule) => rule.should_run(ctx), Self::TypescriptNoWrapperObjectTypes(rule) => rule.should_run(ctx), @@ -14763,6 +14784,7 @@ impl RuleEnum { TypescriptNoUnsafeTypeAssertion::IS_TSGOLINT_RULE } Self::TypescriptNoUnsafeUnaryMinus(_) => TypescriptNoUnsafeUnaryMinus::IS_TSGOLINT_RULE, + Self::TypescriptNoUseBeforeDefine(_) => TypescriptNoUseBeforeDefine::IS_TSGOLINT_RULE, Self::TypescriptNoUselessEmptyExport(_) => { TypescriptNoUselessEmptyExport::IS_TSGOLINT_RULE } @@ -15685,6 +15707,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(_) => TypescriptNoUnsafeReturn::HAS_CONFIG, Self::TypescriptNoUnsafeTypeAssertion(_) => TypescriptNoUnsafeTypeAssertion::HAS_CONFIG, Self::TypescriptNoUnsafeUnaryMinus(_) => TypescriptNoUnsafeUnaryMinus::HAS_CONFIG, + Self::TypescriptNoUseBeforeDefine(_) => TypescriptNoUseBeforeDefine::HAS_CONFIG, Self::TypescriptNoUselessEmptyExport(_) => TypescriptNoUselessEmptyExport::HAS_CONFIG, Self::TypescriptNoVarRequires(_) => TypescriptNoVarRequires::HAS_CONFIG, Self::TypescriptNoWrapperObjectTypes(_) => TypescriptNoWrapperObjectTypes::HAS_CONFIG, @@ -16456,6 +16479,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(rule) => rule.types_info(), Self::TypescriptNoUnsafeTypeAssertion(rule) => rule.types_info(), Self::TypescriptNoUnsafeUnaryMinus(rule) => rule.types_info(), + Self::TypescriptNoUseBeforeDefine(rule) => rule.types_info(), Self::TypescriptNoUselessEmptyExport(rule) => rule.types_info(), Self::TypescriptNoVarRequires(rule) => rule.types_info(), Self::TypescriptNoWrapperObjectTypes(rule) => rule.types_info(), @@ -17133,6 +17157,7 @@ impl RuleEnum { Self::TypescriptNoUnsafeReturn(rule) => rule.run_info(), Self::TypescriptNoUnsafeTypeAssertion(rule) => rule.run_info(), Self::TypescriptNoUnsafeUnaryMinus(rule) => rule.run_info(), + Self::TypescriptNoUseBeforeDefine(rule) => rule.run_info(), Self::TypescriptNoUselessEmptyExport(rule) => rule.run_info(), Self::TypescriptNoVarRequires(rule) => rule.run_info(), Self::TypescriptNoWrapperObjectTypes(rule) => rule.run_info(), @@ -17874,6 +17899,7 @@ pub static RULES: std::sync::LazyLock> = std::sync::LazyLock::new( RuleEnum::TypescriptNoUnsafeReturn(TypescriptNoUnsafeReturn::default()), RuleEnum::TypescriptNoUnsafeTypeAssertion(TypescriptNoUnsafeTypeAssertion::default()), RuleEnum::TypescriptNoUnsafeUnaryMinus(TypescriptNoUnsafeUnaryMinus::default()), + RuleEnum::TypescriptNoUseBeforeDefine(TypescriptNoUseBeforeDefine::default()), RuleEnum::TypescriptNoUselessEmptyExport(TypescriptNoUselessEmptyExport::default()), RuleEnum::TypescriptNoVarRequires(TypescriptNoVarRequires::default()), RuleEnum::TypescriptNoWrapperObjectTypes(TypescriptNoWrapperObjectTypes::default()), diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 76239f361676c..302e2d6ef8368 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -274,6 +274,7 @@ pub(crate) mod typescript { pub mod no_unsafe_return; pub mod no_unsafe_type_assertion; pub mod no_unsafe_unary_minus; + pub mod no_use_before_define; pub mod no_useless_empty_export; pub mod no_var_requires; pub mod no_wrapper_object_types; diff --git a/crates/oxc_linter/src/rules/typescript/no_use_before_define.rs b/crates/oxc_linter/src/rules/typescript/no_use_before_define.rs new file mode 100644 index 0000000000000..7039eb1a5c576 --- /dev/null +++ b/crates/oxc_linter/src/rules/typescript/no_use_before_define.rs @@ -0,0 +1,1533 @@ +use oxc_ast::{ + AstKind, + ast::{BindingPattern, ForStatementLeft, IdentifierReference, ModuleExportName}, +}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::{GetSpan, Span}; +use oxc_syntax::{ + reference::Reference, + scope::ScopeId, + symbol::{SymbolFlags, SymbolId}, +}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{ + AstNode, + context::{ContextHost, LintContext}, + rule::{DefaultRuleConfig, Rule}, +}; + +fn no_use_before_define_diagnostic( + name: &str, + usage_span: Span, + declaration_span: Option, +) -> OxcDiagnostic { + let diagnostic = OxcDiagnostic::warn(format!("'{name}' was used before it was defined.")) + .with_label(usage_span.primary_label("used here")) + .with_help("Move the declaration before any references to it, or remove the reference if it is not needed."); + if let Some(declaration_span) = declaration_span.filter(|span| !span.is_unspanned()) { + diagnostic.and_label(declaration_span.label("defined here")) + } else { + diagnostic + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase", default, deny_unknown_fields)] +pub struct NoUseBeforeDefineConfig { + /// Allow named exports that appear before declaration. + allow_named_exports: bool, + /// Check class declarations. + classes: bool, + /// Check enum declarations. + enums: bool, + /// Check function declarations. + functions: bool, + /// Ignore usages that are type-only references. + ignore_type_references: bool, + /// Check type aliases, interfaces, and type parameters. + typedefs: bool, + /// Check variable declarations. + variables: bool, +} + +impl Default for NoUseBeforeDefineConfig { + fn default() -> Self { + Self { + allow_named_exports: false, + classes: true, + enums: true, + functions: true, + ignore_type_references: true, + typedefs: true, + variables: true, + } + } +} + +#[derive(Debug, Default, Clone)] +pub struct NoUseBeforeDefine(NoUseBeforeDefineConfig); + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallows using variables before they are defined. + /// + /// ### Why is this bad? + /// + /// Referencing identifiers before their declarations can hide bugs and + /// make code order-dependent and difficult to reason about. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```ts + /// new A(); + /// var A = class {}; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```ts + /// var A = class {}; + /// new A(); + /// ``` + NoUseBeforeDefine, + typescript, + restriction, + config = NoUseBeforeDefineConfig, +); + +impl Rule for NoUseBeforeDefine { + fn from_configuration(value: serde_json::Value) -> Result { + serde_json::from_value::>(value) + .map(DefaultRuleConfig::into_inner) + .map(Self) + } + + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::IdentifierReference(identifier) = node.kind() else { + return; + }; + + let reference = ctx.scoping().get_reference(identifier.reference_id()); + let is_named_export = is_named_exports(node, identifier, ctx); + if is_named_export { + if self.0.allow_named_exports { + return; + } + + let symbol_id = reference.symbol_id(); + let should_report = symbol_id + .is_none_or(|symbol_id| !is_defined_before_use(symbol_id, reference, node, ctx)); + if should_report { + ctx.diagnostic(no_use_before_define_diagnostic( + identifier.name.as_str(), + identifier.span, + symbol_id.map(|symbol_id| ctx.scoping().symbol_span(symbol_id)), + )); + } + return; + } + + let Some(symbol_id) = reference.symbol_id() else { + if let Some(declaration_span) = + unresolved_initializer_reference_declaration_span(identifier, node, ctx) + { + ctx.diagnostic(no_use_before_define_diagnostic( + identifier.name.as_str(), + identifier.span, + Some(declaration_span), + )); + } + return; + }; + + if is_defined_before_use(symbol_id, reference, node, ctx) + || !is_forbidden(self.0, symbol_id, reference, node, ctx) + || is_class_ref_in_class_decorator(symbol_id, node, ctx) + || is_function_type_scope(reference.scope_id(), ctx) + { + return; + } + + ctx.diagnostic(no_use_before_define_diagnostic( + identifier.name.as_str(), + identifier.span, + Some(ctx.scoping().symbol_span(symbol_id)), + )); + } + + fn should_run(&self, ctx: &ContextHost) -> bool { + ctx.source_type().is_typescript() + } +} + +fn is_forbidden( + options: NoUseBeforeDefineConfig, + symbol_id: SymbolId, + reference: &Reference, + reference_node: &AstNode<'_>, + ctx: &LintContext<'_>, +) -> bool { + if options.ignore_type_references && is_type_reference(reference, reference_node, ctx) { + return false; + } + + let symbol_flags = ctx.scoping().symbol_flags(symbol_id); + if symbol_flags.is_function() { + return options.functions; + } + + if is_outer_class(symbol_id, symbol_flags, reference.scope_id(), ctx) { + return options.classes; + } + + if is_outer_variable(symbol_id, reference.scope_id(), ctx) { + return options.variables; + } + + if is_outer_enum(symbol_id, symbol_flags, reference.scope_id(), ctx) { + return options.enums; + } + + if is_typedef(symbol_id, ctx) { + return options.typedefs; + } + + true +} + +fn is_outer_class( + symbol_id: SymbolId, + symbol_flags: SymbolFlags, + reference_scope_id: ScopeId, + ctx: &LintContext<'_>, +) -> bool { + symbol_flags.is_class() + && get_parent_variable_scope(ctx.scoping().symbol_scope_id(symbol_id), ctx) + != get_parent_variable_scope(reference_scope_id, ctx) +} + +fn is_outer_variable( + symbol_id: SymbolId, + reference_scope_id: ScopeId, + ctx: &LintContext<'_>, +) -> bool { + matches!(ctx.symbol_declaration(symbol_id).kind(), AstKind::VariableDeclarator(_)) + && get_parent_variable_scope(ctx.scoping().symbol_scope_id(symbol_id), ctx) + != get_parent_variable_scope(reference_scope_id, ctx) +} + +fn is_outer_enum( + symbol_id: SymbolId, + symbol_flags: SymbolFlags, + reference_scope_id: ScopeId, + ctx: &LintContext<'_>, +) -> bool { + symbol_flags.is_enum() + && get_parent_variable_scope(ctx.scoping().symbol_scope_id(symbol_id), ctx) + != get_parent_variable_scope(reference_scope_id, ctx) +} + +fn is_typedef(symbol_id: SymbolId, ctx: &LintContext<'_>) -> bool { + matches!( + ctx.symbol_declaration(symbol_id).kind(), + AstKind::TSTypeAliasDeclaration(_) + | AstKind::TSInterfaceDeclaration(_) + | AstKind::TSTypeParameter(_) + ) +} + +fn is_named_exports( + node: &AstNode<'_>, + identifier: &IdentifierReference<'_>, + ctx: &LintContext<'_>, +) -> bool { + let parent = ctx.nodes().parent_node(node.id()); + matches!( + parent.kind(), + AstKind::ExportSpecifier(export_specifier) + if matches!( + &export_specifier.local, + ModuleExportName::IdentifierReference(local) + if local.reference_id() == identifier.reference_id() + ) + ) +} + +fn is_type_reference( + reference: &Reference, + reference_node: &AstNode<'_>, + ctx: &LintContext<'_>, +) -> bool { + reference.is_type() || reference_contains_type_query(reference_node, ctx) +} + +fn reference_contains_type_query(reference_node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool { + let mut node = reference_node; + + loop { + node = match node.kind() { + AstKind::TSTypeQuery(_) => return true, + AstKind::TSQualifiedName(_) | AstKind::IdentifierReference(_) => { + ctx.nodes().parent_node(node.id()) + } + _ => return false, + }; + } +} + +fn is_defined_before_use( + symbol_id: SymbolId, + reference: &Reference, + reference_node: &AstNode<'_>, + ctx: &LintContext<'_>, +) -> bool { + let defined_before_reference = + ctx.scoping().symbol_span(symbol_id).end <= reference_node.kind().span().end; + defined_before_reference + && !(reference.is_value() && is_in_initializer(symbol_id, reference, reference_node, ctx)) +} + +fn unresolved_initializer_reference_declaration_span( + identifier: &IdentifierReference<'_>, + reference_node: &AstNode<'_>, + ctx: &LintContext<'_>, +) -> Option { + let name = identifier.name.as_str(); + initializer_reference_declaration_span(reference_node, ctx, |pattern| { + binding_pattern_name_span(pattern, name) + }) +} + +fn binding_pattern_name_span(pattern: &BindingPattern<'_>, name: &str) -> Option { + let mut declaration_span = None; + pattern.all_binding_identifiers(&mut |identifier| { + if identifier.name == name { + declaration_span = Some(identifier.span); + false + } else { + true + } + }); + declaration_span +} + +fn for_statement_left_declaration_span( + left: &ForStatementLeft<'_>, + find_declaration_span: &mut F, +) -> Option +where + F: FnMut(&BindingPattern<'_>) -> Option, +{ + let ForStatementLeft::VariableDeclaration(variable_declaration) = left else { + return None; + }; + variable_declaration + .declarations + .iter() + .find_map(|declarator| find_declaration_span(&declarator.id)) +} + +fn initializer_reference_declaration_span( + reference_node: &AstNode<'_>, + ctx: &LintContext<'_>, + mut find_declaration_span: F, +) -> Option +where + F: FnMut(&BindingPattern<'_>) -> Option, +{ + let reference_location = reference_node.kind().span().end; + + for ancestor in ctx.nodes().ancestors(reference_node.id()) { + match ancestor.kind() { + AstKind::FormalParameter(formal_parameter) => { + if let Some(declaration_span) = find_declaration_span(&formal_parameter.pattern) + && formal_parameter + .initializer + .as_ref() + .is_some_and(|init| is_in_range(init.span(), reference_location)) + { + return Some(declaration_span); + } + } + AstKind::VariableDeclarator(declarator) => { + let Some(declaration_span) = find_declaration_span(&declarator.id) else { + continue; + }; + + if declarator + .init + .as_ref() + .is_some_and(|init| is_in_range(init.span(), reference_location)) + { + return Some(declaration_span); + } + + let parent = ctx.nodes().parent_node(ancestor.id()); + if let AstKind::VariableDeclaration(_) = parent.kind() { + let grand_parent = ctx.nodes().parent_node(parent.id()); + if matches!( + grand_parent.kind(), + AstKind::ForInStatement(for_in) + if is_in_range(for_in.right.span(), reference_location) + ) || matches!( + grand_parent.kind(), + AstKind::ForOfStatement(for_of) + if is_in_range(for_of.right.span(), reference_location) + ) { + return Some(declaration_span); + } + } + + break; + } + AstKind::AssignmentPattern(assignment_pattern) => { + if let Some(declaration_span) = find_declaration_span(&assignment_pattern.left) + && is_in_range(assignment_pattern.right.span(), reference_location) + { + return Some(declaration_span); + } + } + AstKind::ForInStatement(for_in) => { + if let Some(declaration_span) = + for_statement_left_declaration_span(&for_in.left, &mut find_declaration_span) + && is_in_range(for_in.right.span(), reference_location) + { + return Some(declaration_span); + } + } + AstKind::ForOfStatement(for_of) => { + if let Some(declaration_span) = + for_statement_left_declaration_span(&for_of.left, &mut find_declaration_span) + && is_in_range(for_of.right.span(), reference_location) + { + return Some(declaration_span); + } + } + AstKind::Function(_) + | AstKind::Class(_) + | AstKind::ArrowFunctionExpression(_) + | AstKind::CatchClause(_) + | AstKind::ImportDeclaration(_) + | AstKind::ExportNamedDeclaration(_) => break, + _ => {} + } + } + + None +} + +fn is_in_initializer( + symbol_id: SymbolId, + _reference: &Reference, + reference_node: &AstNode<'_>, + ctx: &LintContext<'_>, +) -> bool { + let declaration_span = ctx.scoping().symbol_span(symbol_id); + initializer_reference_declaration_span(reference_node, ctx, |pattern| { + pattern.span().contains_inclusive(declaration_span).then_some(declaration_span) + }) + .is_some() +} + +fn is_class_ref_in_class_decorator( + symbol_id: SymbolId, + reference_node: &AstNode<'_>, + ctx: &LintContext<'_>, +) -> bool { + if !ctx.scoping().symbol_flags(symbol_id).is_class() { + return false; + } + + let AstKind::Class(class) = ctx.symbol_declaration(symbol_id).kind() else { + return false; + }; + + if class.decorators.is_empty() { + return false; + } + + let reference_span = reference_node.kind().span(); + class.decorators.iter().any(|decorator| decorator.span.contains_inclusive(reference_span)) +} + +fn is_function_type_scope(scope_id: ScopeId, ctx: &LintContext<'_>) -> bool { + let scope_node_id = ctx.scoping().get_node_id(scope_id); + matches!( + ctx.nodes().kind(scope_node_id), + AstKind::TSFunctionType(_) + | AstKind::TSConstructorType(_) + | AstKind::TSCallSignatureDeclaration(_) + | AstKind::TSMethodSignature(_) + | AstKind::TSConstructSignatureDeclaration(_) + ) +} + +fn get_parent_variable_scope(scope_id: ScopeId, ctx: &LintContext<'_>) -> ScopeId { + ctx.scoping() + .scope_ancestors(scope_id) + .find(|scope_id| ctx.scoping().scope_flags(*scope_id).is_var()) + .unwrap_or_else(|| ctx.scoping().root_scope_id()) +} + +fn is_in_range(span: Span, location: u32) -> bool { + span.start <= location && location <= span.end +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ( + " + type foo = 1; + const x: foo = 1; + ", + None, + ), + ( + " + type foo = 1; + type bar = foo; + ", + None, + ), + ( + " + interface Foo {} + const x: Foo = {}; + ", + None, + ), + ( + " + var a = 10; + alert(a); + ", + None, + ), + ( + " + function b(a) { + alert(a); + } + ", + None, + ), + ("Object.hasOwnProperty.call(a);", None), + ( + " + function a() { + alert(arguments); + } + ", + None, + ), + ("declare function a();", None), + ( + " + declare class a { + foo(); + } + ", + None, + ), + ("const updatedAt = data?.updatedAt;", None), + ( + " + function f() { + return function t() {}; + } + f()?.(); + ", + None, + ), + ( + " + var a = { b: 5 }; + alert(a?.b); + ", + None, + ), + ( + " + a(); + function a() { + alert(arguments); + } + ", + Some(serde_json::json!([{ "functions": false }])), + ), + ( + " + (() => { + var a = 42; + alert(a); + })(); + ", + None, + ), // { parserOptions }, + ( + " + a(); + try { + throw new Error(); + } catch (a) {} + ", + None, + ), + ( + " + class A {} + new A(); + ", + None, + ), // { parserOptions }, + ( + " + var a = 0, + b = a; + ", + None, + ), + ("var { a = 0, b = a } = {};", None), // { parserOptions }, + ("var [a = 0, b = a] = {};", None), // { parserOptions }, + ( + " + function foo() { + foo(); + } + ", + None, + ), + ( + " + var foo = function () { + foo(); + }; + ", + None, + ), + ( + " + var a; + for (a in a) { + } + ", + None, + ), + ( + " + var a; + for (a of a) { + } + ", + None, + ), // { parserOptions }, + ( + " + 'use strict'; + a(); + { + function a() {} + } + ", + None, + ), // { parserOptions }, + ( + " + 'use strict'; + { + a(); + function a() {} + } + ", + Some(serde_json::json!([{ "functions": false }])), + ), // { parserOptions }, + ( + " + switch (foo) { + case 1: { + a(); + } + default: { + let a; + } + } + ", + None, + ), // { parserOptions }, + ( + " + a(); + { + let a = function () {}; + } + ", + None, + ), // { parserOptions }, + ( + " + a(); + function a() { + alert(arguments); + } + ", + Some(serde_json::json!([{ "functions": false }])), + ), + ( + " + 'use strict'; + { + a(); + function a() {} + } + ", + Some(serde_json::json!([{ "functions": false }])), + ), // { parserOptions }, + ( + " + function foo() { + new A(); + } + class A {} + ", + Some(serde_json::json!([{ "classes": false }])), + ), // { parserOptions }, + ( + " + function foo() { + bar; + } + var bar; + ", + Some(serde_json::json!([{ "variables": false }])), + ), + ( + " + var foo = () => bar; + var bar; + ", + Some(serde_json::json!([{ "variables": false }])), + ), // { parserOptions }, + ( + " + var x: Foo = 2; + type Foo = string | number; + ", + Some(serde_json::json!([{ "typedefs": false }])), + ), + ( + " + interface Bar { + type: typeof Foo; + } + + const Foo = 2; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": true }])), + ), + ( + " + interface Bar { + type: typeof Foo.FOO; + } + + class Foo { + public static readonly FOO = ''; + } + ", + Some(serde_json::json!([{ "ignoreTypeReferences": true }])), + ), + ( + " + interface Bar { + type: typeof Foo.Bar.Baz; + } + + const Foo = { + Bar: { + Baz: 1, + }, + }; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": true }])), + ), + ( + " + interface ITest { + first: boolean; + second: string; + third: boolean; + } + + let first = () => console.log('first'); + + export let second = () => console.log('second'); + + export namespace Third { + export let third = () => console.log('third'); + } + ", + None, + ), // { "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, }, + ( + " + function test(file: Blob) { + const slice: typeof file.slice = + file.slice || (file as any).webkitSlice || (file as any).mozSlice; + return slice; + } + ", + None, + ), + ( + " + interface Foo { + bar: string; + } + const bar = 'blah'; + ", + None, + ), + ( + " + function foo(): Foo { + return Foo.FOO; + } + + enum Foo { + FOO, + } + ", + Some(serde_json::json!([{ "enums": false }])), + ), + ( + " + let foo: Foo; + + enum Foo { + FOO, + } + ", + Some(serde_json::json!([{ "enums": false }])), + ), + ( + " + class Test { + foo(args: Foo): Foo { + return Foo.FOO; + } + } + + enum Foo { + FOO, + } + ", + Some(serde_json::json!([{ "enums": false }])), + ), + ( + " + export { a }; + const a = 1; + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { a as b }; + const a = 1; + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { a, b }; + let a, b; + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { a }; + var a; + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { f }; + function f() {} + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { C }; + class C {} + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { Foo }; + + enum Foo { + BAR, + } + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { Foo }; + + namespace Foo { + export let bar = () => console.log('bar'); + } + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { Foo, baz }; + + enum Foo { + BAR, + } + + let baz: Enum; + enum Enum {} + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + import * as React from 'react'; + +
; + ", + None, + ), // { "parserOptions": { "ecmaFeatures": { "jsx": true, }, "sourceType": "module", }, }, + ( + " + import React from 'react'; + +
; + ", + None, + ), // { "parserOptions": { "ecmaFeatures": { "jsx": true, }, "sourceType": "module", }, }, + ( + " + import { h } from 'preact'; + +
; + ", + None, + ), // { "parserOptions": { "ecmaFeatures": { "jsx": true, }, "jsxPragma": "h", "sourceType": "module", }, }, + ( + " + const React = require('react'); + +
; + ", + None, + ), // { "parserOptions": { "ecmaFeatures": { "jsx": true, }, }, }, + ( + " + type T = (value: unknown) => value is Id; + ", + None, + ), + ( + " + global.foo = true; + + declare global { + namespace NodeJS { + interface Global { + foo?: boolean; + } + } + } + ", + None, + ), + ( + " + @Directive({ + selector: '[rcCidrIpPattern]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: CidrIpPatternDirective, + multi: true, + }, + ], + }) + export class CidrIpPatternDirective implements Validator {} + ", + None, + ), + ( + " + @Directive({ + selector: '[rcCidrIpPattern]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: CidrIpPatternDirective, + multi: true, + }, + ], + }) + export class CidrIpPatternDirective implements Validator {} + ", + Some(serde_json::json!([ { "classes": false, }, ])), + ), + ( + " + class A { + constructor(printName) { + this.printName = printName; + } + + openPort(printerName = this.printerName) { + this.tscOcx.ActiveXopenport(printerName); + + return this; + } + } + ", + None, + ), + ( + " + const obj = { + foo: 'foo-value', + bar: 'bar-value', + } satisfies { + [key in 'foo' | 'bar']: `${key}-value`; + }; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": false }])), + ), + ( + " + const obj = { + foo: 'foo-value', + bar: 'bar-value', + } as { + [key in 'foo' | 'bar']: `${key}-value`; + }; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": false }])), + ), + ( + " + const obj = { + foo: { + foo: 'foo', + } as { + [key in 'foo' | 'bar']: key; + }, + }; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": false }])), + ), + ( + " + const foo = { + bar: 'bar', + } satisfies { + bar: typeof baz; + }; + + const baz = ''; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": true }])), + ), + ( + " + namespace A.X.Y {} + + import Z = A.X.Y; + + const X = 23; + ", + None, + ), + ]; + + let fail = vec![ + ( + " + a++; + var a = 19; + ", + None, + ), // { "parserOptions": { "sourceType": "module" }, }, + ( + " + a++; + var a = 19; + ", + None, + ), // { parserOptions }, + ( + " + a++; + var a = 19; + ", + None, + ), + ( + " + a(); + var a = function () {}; + ", + None, + ), + ( + " + alert(a[1]); + var a = [1, 3]; + ", + None, + ), + ( + " + a(); + function a() { + alert(b); + var b = 10; + a(); + } + ", + None, + ), + ( + " + a(); + var a = function () {}; + ", + Some(serde_json::json!([{ "functions": false }])), + ), + ( + " + (() => { + alert(a); + var a = 42; + })(); + ", + None, + ), // { parserOptions }, + ( + " + (() => a())(); + function a() {} + ", + None, + ), // { parserOptions }, + ( + " + a(); + try { + throw new Error(); + } catch (foo) { + var a; + } + ", + None, + ), + ( + " + var f = () => a; + var a; + ", + None, + ), // { parserOptions }, + ( + " + new A(); + class A {} + ", + None, + ), // { parserOptions }, + ( + " + function foo() { + new A(); + } + class A {} + ", + None, + ), // { parserOptions }, + ( + " + new A(); + var A = class {}; + ", + None, + ), // { parserOptions }, + ( + " + function foo() { + new A(); + } + var A = class {}; + ", + None, + ), // { parserOptions }, + ( + " + a++; + { + var a; + } + ", + None, + ), // { parserOptions }, + ( + " + 'use strict'; + { + a(); + function a() {} + } + ", + None, + ), // { parserOptions }, + ( + " + { + a; + let a = 1; + } + ", + None, + ), // { parserOptions }, + ( + " + switch (foo) { + case 1: + a(); + default: + let a; + } + ", + None, + ), // { parserOptions }, + ( + " + if (true) { + function foo() { + a; + } + let a; + } + ", + None, + ), // { parserOptions }, + ( + " + a(); + var a = function () {}; + ", + Some(serde_json::json!([{ "classes": false, "functions": false }])), + ), + ( + " + new A(); + var A = class {}; + ", + Some(serde_json::json!([{ "classes": false }])), + ), // { parserOptions }, + ( + " + function foo() { + new A(); + } + var A = class {}; + ", + Some(serde_json::json!([{ "classes": false }])), + ), // { parserOptions }, + ("var a = a;", None), + ("let a = a + b;", None), // { parserOptions }, + ("const a = foo(a);", None), // { parserOptions }, + ("function foo(a = a) {}", None), // { parserOptions }, + ("var { a = a } = [];", None), // { parserOptions }, + ("var [a = a] = [];", None), // { parserOptions }, + ("var { b = a, a } = {};", None), // { parserOptions }, + ("var [b = a, a] = {};", None), // { parserOptions }, + ("var { a = 0 } = a;", None), // { parserOptions }, + ("var [a = 0] = a;", None), // { parserOptions }, + ( + " + for (var a in a) { + } + ", + None, + ), + ( + " + for (var a of a) { + } + ", + None, + ), // { parserOptions }, + ( + " + interface Bar { + type: typeof Foo; + } + + const Foo = 2; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": false }])), + ), + ( + " + interface Bar { + type: typeof Foo.FOO; + } + + class Foo { + public static readonly FOO = ''; + } + ", + Some(serde_json::json!([{ "ignoreTypeReferences": false }])), + ), + ( + " + interface Bar { + type: typeof Foo.Bar.Baz; + } + + const Foo = { + Bar: { + Baz: 1, + }, + }; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": false }])), + ), + ( + " + const foo = { + bar: 'bar', + } satisfies { + bar: typeof baz; + }; + + const baz = ''; + ", + Some(serde_json::json!([{ "ignoreTypeReferences": false }])), + ), + ( + " + function foo() { + bar; + var bar = 1; + } + var bar; + ", + Some(serde_json::json!([{ "variables": false }])), + ), // { parserOptions }, + ( + " + class Test { + foo(args: Foo): Foo { + return Foo.FOO; + } + } + + enum Foo { + FOO, + } + ", + Some(serde_json::json!([{ "enums": true }])), + ), + ( + " + function foo(): Foo { + return Foo.FOO; + } + + enum Foo { + FOO, + } + ", + Some(serde_json::json!([{ "enums": true }])), + ), + ( + " + const foo = Foo.Foo; + + enum Foo { + FOO, + } + ", + Some(serde_json::json!([{ "enums": true }])), + ), + ( + " + export { a }; + const a = 1; + ", + None, + ), // { parserOptions }, + ( + " + export { a }; + const a = 1; + ", + Some(serde_json::json!([{}])), + ), // { parserOptions }, + ( + " + export { a }; + const a = 1; + ", + Some(serde_json::json!([{ "allowNamedExports": false }])), + ), // { parserOptions }, + ( + " + export { a }; + const a = 1; + ", + Some(serde_json::json!([{ "functions": false }])), + ), // { parserOptions }, + ( + " + export { a as b }; + const a = 1; + ", + None, + ), // { parserOptions }, + ( + " + export { a, b }; + let a, b; + ", + None, + ), // { parserOptions }, + ( + " + export { a }; + var a; + ", + None, + ), // { parserOptions }, + ( + " + export { f }; + function f() {} + ", + None, + ), // { parserOptions }, + ( + " + export { C }; + class C {} + ", + None, + ), // { parserOptions }, + ( + " + export const foo = a; + const a = 1; + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export function foo() { + return a; + } + const a = 1; + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export class C { + foo() { + return a; + } + } + const a = 1; + ", + Some(serde_json::json!([{ "allowNamedExports": true }])), + ), // { parserOptions }, + ( + " + export { Foo }; + + enum Foo { + BAR, + } + ", + None, + ), // { parserOptions }, + ( + " + export { Foo }; + + namespace Foo { + export let bar = () => console.log('bar'); + } + ", + None, + ), // { parserOptions }, + ( + " + export { Foo, baz }; + + enum Foo { + BAR, + } + + let baz: Enum; + enum Enum {} + ", + Some(serde_json::json!([{ "allowNamedExports": false, "ignoreTypeReferences": true }])), + ), // { parserOptions }, + ( + " + f(); + function f() {} + ", + None, + ), + ( + " + alert(a); + var a = 10; + ", + None, + ), + ( + " + f()?.(); + function f() { + return function t() {}; + } + ", + None, + ), + ( + " + alert(a?.b); + var a = { b: 5 }; + ", + None, + ), + ]; + + Tester::new(NoUseBeforeDefine::NAME, NoUseBeforeDefine::PLUGIN, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/typescript_no_use_before_define.snap b/crates/oxc_linter/src/snapshots/typescript_no_use_before_define.snap new file mode 100644 index 0000000000000..131932ada4953 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/typescript_no_use_before_define.snap @@ -0,0 +1,838 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a++; + · ┬ + · ╰── used here + 3 │ var a = 19; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a++; + · ┬ + · ╰── used here + 3 │ var a = 19; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a++; + · ┬ + · ╰── used here + 3 │ var a = 19; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a(); + · ┬ + · ╰── used here + 3 │ var a = function () {}; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:19] + 1 │ + 2 │ alert(a[1]); + · ┬ + · ╰── used here + 3 │ var a = [1, 3]; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a(); + · ┬ + · ╰── used here + 3 │ function a() { + · ┬ + · ╰── defined here + 4 │ alert(b); + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'b' was used before it was defined. + ╭─[no_use_before_define.tsx:4:21] + 3 │ function a() { + 4 │ alert(b); + · ┬ + · ╰── used here + 5 │ var b = 10; + · ┬ + · ╰── defined here + 6 │ a(); + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a(); + · ┬ + · ╰── used here + 3 │ var a = function () {}; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:3:21] + 2 │ (() => { + 3 │ alert(a); + · ┬ + · ╰── used here + 4 │ var a = 42; + · ┬ + · ╰── defined here + 5 │ })(); + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:20] + 1 │ + 2 │ (() => a())(); + · ┬ + · ╰── used here + 3 │ function a() {} + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a(); + · ┬ + · ╰── used here + 3 │ try { + ╰──── + ╭─[no_use_before_define.tsx:6:19] + 5 │ } catch (foo) { + 6 │ var a; + · ┬ + · ╰── defined here + 7 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:27] + 1 │ + 2 │ var f = () => a; + · ┬ + · ╰── used here + 3 │ var a; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'A' was used before it was defined. + ╭─[no_use_before_define.tsx:2:17] + 1 │ + 2 │ new A(); + · ┬ + · ╰── used here + 3 │ class A {} + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'A' was used before it was defined. + ╭─[no_use_before_define.tsx:3:19] + 2 │ function foo() { + 3 │ new A(); + · ┬ + · ╰── used here + 4 │ } + 5 │ class A {} + · ┬ + · ╰── defined here + 6 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'A' was used before it was defined. + ╭─[no_use_before_define.tsx:2:17] + 1 │ + 2 │ new A(); + · ┬ + · ╰── used here + 3 │ var A = class {}; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'A' was used before it was defined. + ╭─[no_use_before_define.tsx:3:19] + 2 │ function foo() { + 3 │ new A(); + · ┬ + · ╰── used here + 4 │ } + 5 │ var A = class {}; + · ┬ + · ╰── defined here + 6 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a++; + · ┬ + · ╰── used here + 3 │ { + 4 │ var a; + · ┬ + · ╰── defined here + 5 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:4:15] + 3 │ { + 4 │ a(); + · ┬ + · ╰── used here + 5 │ function a() {} + · ┬ + · ╰── defined here + 6 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:3:15] + 2 │ { + 3 │ a; + · ┬ + · ╰── used here + 4 │ let a = 1; + · ┬ + · ╰── defined here + 5 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:4:17] + 3 │ case 1: + 4 │ a(); + · ┬ + · ╰── used here + 5 │ default: + 6 │ let a; + · ┬ + · ╰── defined here + 7 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:4:17] + 3 │ function foo() { + 4 │ a; + · ┬ + · ╰── used here + 5 │ } + 6 │ let a; + · ┬ + · ╰── defined here + 7 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ a(); + · ┬ + · ╰── used here + 3 │ var a = function () {}; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'A' was used before it was defined. + ╭─[no_use_before_define.tsx:2:17] + 1 │ + 2 │ new A(); + · ┬ + · ╰── used here + 3 │ var A = class {}; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'A' was used before it was defined. + ╭─[no_use_before_define.tsx:3:19] + 2 │ function foo() { + 3 │ new A(); + · ┬ + · ╰── used here + 4 │ } + 5 │ var A = class {}; + · ┬ + · ╰── defined here + 6 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:9] + 1 │ var a = a; + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:9] + 1 │ let a = a + b; + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:15] + 1 │ const a = foo(a); + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:18] + 1 │ function foo(a = a) {} + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:11] + 1 │ var { a = a } = []; + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:10] + 1 │ var [a = a] = []; + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:11] + 1 │ var { b = a, a } = {}; + · ┬ ┬ + · │ ╰── defined here + · ╰── used here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:10] + 1 │ var [b = a, a] = {}; + · ┬ ┬ + · │ ╰── defined here + · ╰── used here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:17] + 1 │ var { a = 0 } = a; + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:1:15] + 1 │ var [a = 0] = a; + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:27] + 1 │ + 2 │ for (var a in a) { + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + 3 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:27] + 1 │ + 2 │ for (var a of a) { + · ┬ ┬ + · │ ╰── used here + · ╰── defined here + 3 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:3:28] + 2 │ interface Bar { + 3 │ type: typeof Foo; + · ─┬─ + · ╰── used here + 4 │ } + 5 │ + 6 │ const Foo = 2; + · ─┬─ + · ╰── defined here + 7 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:3:28] + 2 │ interface Bar { + 3 │ type: typeof Foo.FOO; + · ─┬─ + · ╰── used here + 4 │ } + 5 │ + 6 │ class Foo { + · ─┬─ + · ╰── defined here + 7 │ public static readonly FOO = ''; + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:3:28] + 2 │ interface Bar { + 3 │ type: typeof Foo.Bar.Baz; + · ─┬─ + · ╰── used here + 4 │ } + 5 │ + 6 │ const Foo = { + · ─┬─ + · ╰── defined here + 7 │ Bar: { + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'baz' was used before it was defined. + ╭─[no_use_before_define.tsx:5:27] + 4 │ } satisfies { + 5 │ bar: typeof baz; + · ─┬─ + · ╰── used here + 6 │ }; + 7 │ + 8 │ const baz = ''; + · ─┬─ + · ╰── defined here + 9 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'bar' was used before it was defined. + ╭─[no_use_before_define.tsx:3:15] + 2 │ function foo() { + 3 │ bar; + · ─┬─ + · ╰── used here + 4 │ var bar = 1; + · ─┬─ + · ╰── defined here + 5 │ } + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:4:24] + 3 │ foo(args: Foo): Foo { + 4 │ return Foo.FOO; + · ─┬─ + · ╰── used here + 5 │ } + 6 │ } + 7 │ + 8 │ enum Foo { + · ─┬─ + · ╰── defined here + 9 │ FOO, + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:3:22] + 2 │ function foo(): Foo { + 3 │ return Foo.FOO; + · ─┬─ + · ╰── used here + 4 │ } + 5 │ + 6 │ enum Foo { + · ─┬─ + · ╰── defined here + 7 │ FOO, + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:2:25] + 1 │ + 2 │ const foo = Foo.Foo; + · ─┬─ + · ╰── used here + 3 │ + 4 │ enum Foo { + · ─┬─ + · ╰── defined here + 5 │ FOO, + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { a }; + · ┬ + · ╰── used here + 3 │ const a = 1; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { a }; + · ┬ + · ╰── used here + 3 │ const a = 1; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { a }; + · ┬ + · ╰── used here + 3 │ const a = 1; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { a }; + · ┬ + · ╰── used here + 3 │ const a = 1; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { a as b }; + · ┬ + · ╰── used here + 3 │ const a = 1; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { a, b }; + · ┬ + · ╰── used here + 3 │ let a, b; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'b' was used before it was defined. + ╭─[no_use_before_define.tsx:2:25] + 1 │ + 2 │ export { a, b }; + · ┬ + · ╰── used here + 3 │ let a, b; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { a }; + · ┬ + · ╰── used here + 3 │ var a; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'f' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { f }; + · ┬ + · ╰── used here + 3 │ function f() {} + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'C' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { C }; + · ┬ + · ╰── used here + 3 │ class C {} + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:32] + 1 │ + 2 │ export const foo = a; + · ┬ + · ╰── used here + 3 │ const a = 1; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:3:22] + 2 │ export function foo() { + 3 │ return a; + · ┬ + · ╰── used here + 4 │ } + 5 │ const a = 1; + · ┬ + · ╰── defined here + 6 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:4:24] + 3 │ foo() { + 4 │ return a; + · ┬ + · ╰── used here + 5 │ } + 6 │ } + 7 │ const a = 1; + · ┬ + · ╰── defined here + 8 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { Foo }; + · ─┬─ + · ╰── used here + 3 │ + 4 │ enum Foo { + · ─┬─ + · ╰── defined here + 5 │ BAR, + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { Foo }; + · ─┬─ + · ╰── used here + 3 │ + 4 │ namespace Foo { + · ─┬─ + · ╰── defined here + 5 │ export let bar = () => console.log('bar'); + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'Foo' was used before it was defined. + ╭─[no_use_before_define.tsx:2:22] + 1 │ + 2 │ export { Foo, baz }; + · ─┬─ + · ╰── used here + 3 │ + 4 │ enum Foo { + · ─┬─ + · ╰── defined here + 5 │ BAR, + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'baz' was used before it was defined. + ╭─[no_use_before_define.tsx:2:27] + 1 │ + 2 │ export { Foo, baz }; + · ─┬─ + · ╰── used here + 3 │ + ╰──── + ╭─[no_use_before_define.tsx:8:17] + 7 │ + 8 │ let baz: Enum; + · ─┬─ + · ╰── defined here + 9 │ enum Enum {} + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'f' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ f(); + · ┬ + · ╰── used here + 3 │ function f() {} + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:19] + 1 │ + 2 │ alert(a); + · ┬ + · ╰── used here + 3 │ var a = 10; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'f' was used before it was defined. + ╭─[no_use_before_define.tsx:2:13] + 1 │ + 2 │ f()?.(); + · ┬ + · ╰── used here + 3 │ function f() { + · ┬ + · ╰── defined here + 4 │ return function t() {}; + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed. + + ⚠ typescript-eslint(no-use-before-define): 'a' was used before it was defined. + ╭─[no_use_before_define.tsx:2:19] + 1 │ + 2 │ alert(a?.b); + · ┬ + · ╰── used here + 3 │ var a = { b: 5 }; + · ┬ + · ╰── defined here + 4 │ + ╰──── + help: Move the declaration before any references to it, or remove the reference if it is not needed.