From 68a662b43021bd7c715dece6374b29e087dcff07 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Fri, 8 May 2026 08:40:00 +0200 Subject: [PATCH 1/2] feat(linter): implement no-implicit-globals --- .../src/generated/rule_runner_impls.rs | 5 + crates/oxc_linter/src/generated/rules_enum.rs | 27 +- crates/oxc_linter/src/rules.rs | 1 + .../src/rules/eslint/no_implicit_globals.rs | 710 +++++++++++++ .../snapshots/eslint_no_implicit_globals.snap | 973 ++++++++++++++++++ 5 files changed, 1715 insertions(+), 1 deletion(-) create mode 100644 crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs create mode 100644 crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index 2eb307f1d928a..a76a002f7281d 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -702,6 +702,11 @@ impl RuleRunner for crate::rules::eslint::no_implicit_coercion::NoImplicitCoerci const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } +impl RuleRunner for crate::rules::eslint::no_implicit_globals::NoImplicitGlobals { + const NODE_TYPES: Option<&AstTypesBitset> = None; + const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnce; +} + impl RuleRunner for crate::rules::eslint::no_import_assign::NoImportAssign { const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[AstType::ImportDeclaration])); diff --git a/crates/oxc_linter/src/generated/rules_enum.rs b/crates/oxc_linter/src/generated/rules_enum.rs index 87f558fae26f6..c466f25c1d7ca 100644 --- a/crates/oxc_linter/src/generated/rules_enum.rs +++ b/crates/oxc_linter/src/generated/rules_enum.rs @@ -81,6 +81,7 @@ pub use crate::rules::eslint::no_fallthrough::NoFallthrough as EslintNoFallthrou pub use crate::rules::eslint::no_func_assign::NoFuncAssign as EslintNoFuncAssign; pub use crate::rules::eslint::no_global_assign::NoGlobalAssign as EslintNoGlobalAssign; pub use crate::rules::eslint::no_implicit_coercion::NoImplicitCoercion as EslintNoImplicitCoercion; +pub use crate::rules::eslint::no_implicit_globals::NoImplicitGlobals as EslintNoImplicitGlobals; pub use crate::rules::eslint::no_import_assign::NoImportAssign as EslintNoImportAssign; pub use crate::rules::eslint::no_inline_comments::NoInlineComments as EslintNoInlineComments; pub use crate::rules::eslint::no_inner_declarations::NoInnerDeclarations as EslintNoInnerDeclarations; @@ -911,6 +912,7 @@ pub enum RuleEnum { EslintNoFuncAssign(EslintNoFuncAssign), EslintNoGlobalAssign(EslintNoGlobalAssign), EslintNoImplicitCoercion(EslintNoImplicitCoercion), + EslintNoImplicitGlobals(EslintNoImplicitGlobals), EslintNoImportAssign(EslintNoImportAssign), EslintNoInlineComments(EslintNoInlineComments), EslintNoInnerDeclarations(EslintNoInnerDeclarations), @@ -1707,7 +1709,8 @@ const ESLINT_NO_FALLTHROUGH_ID: usize = ESLINT_NO_EXTRA_LABEL_ID + 1usize; const ESLINT_NO_FUNC_ASSIGN_ID: usize = ESLINT_NO_FALLTHROUGH_ID + 1usize; const ESLINT_NO_GLOBAL_ASSIGN_ID: usize = ESLINT_NO_FUNC_ASSIGN_ID + 1usize; const ESLINT_NO_IMPLICIT_COERCION_ID: usize = ESLINT_NO_GLOBAL_ASSIGN_ID + 1usize; -const ESLINT_NO_IMPORT_ASSIGN_ID: usize = ESLINT_NO_IMPLICIT_COERCION_ID + 1usize; +const ESLINT_NO_IMPLICIT_GLOBALS_ID: usize = ESLINT_NO_IMPLICIT_COERCION_ID + 1usize; +const ESLINT_NO_IMPORT_ASSIGN_ID: usize = ESLINT_NO_IMPLICIT_GLOBALS_ID + 1usize; const ESLINT_NO_INLINE_COMMENTS_ID: usize = ESLINT_NO_IMPORT_ASSIGN_ID + 1usize; const ESLINT_NO_INNER_DECLARATIONS_ID: usize = ESLINT_NO_INLINE_COMMENTS_ID + 1usize; const ESLINT_NO_INVALID_REGEXP_ID: usize = ESLINT_NO_INNER_DECLARATIONS_ID + 1usize; @@ -2599,6 +2602,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => ESLINT_NO_FUNC_ASSIGN_ID, Self::EslintNoGlobalAssign(_) => ESLINT_NO_GLOBAL_ASSIGN_ID, Self::EslintNoImplicitCoercion(_) => ESLINT_NO_IMPLICIT_COERCION_ID, + Self::EslintNoImplicitGlobals(_) => ESLINT_NO_IMPLICIT_GLOBALS_ID, Self::EslintNoImportAssign(_) => ESLINT_NO_IMPORT_ASSIGN_ID, Self::EslintNoInlineComments(_) => ESLINT_NO_INLINE_COMMENTS_ID, Self::EslintNoInnerDeclarations(_) => ESLINT_NO_INNER_DECLARATIONS_ID, @@ -3512,6 +3516,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => EslintNoFuncAssign::NAME, Self::EslintNoGlobalAssign(_) => EslintNoGlobalAssign::NAME, Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::NAME, + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::NAME, Self::EslintNoImportAssign(_) => EslintNoImportAssign::NAME, Self::EslintNoInlineComments(_) => EslintNoInlineComments::NAME, Self::EslintNoInnerDeclarations(_) => EslintNoInnerDeclarations::NAME, @@ -4413,6 +4418,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => EslintNoFuncAssign::CATEGORY, Self::EslintNoGlobalAssign(_) => EslintNoGlobalAssign::CATEGORY, Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::CATEGORY, + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::CATEGORY, Self::EslintNoImportAssign(_) => EslintNoImportAssign::CATEGORY, Self::EslintNoInlineComments(_) => EslintNoInlineComments::CATEGORY, Self::EslintNoInnerDeclarations(_) => EslintNoInnerDeclarations::CATEGORY, @@ -5367,6 +5373,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => EslintNoFuncAssign::FIX, Self::EslintNoGlobalAssign(_) => EslintNoGlobalAssign::FIX, Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::FIX, + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::FIX, Self::EslintNoImportAssign(_) => EslintNoImportAssign::FIX, Self::EslintNoInlineComments(_) => EslintNoInlineComments::FIX, Self::EslintNoInnerDeclarations(_) => EslintNoInnerDeclarations::FIX, @@ -6277,6 +6284,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => EslintNoFuncAssign::documentation(), Self::EslintNoGlobalAssign(_) => EslintNoGlobalAssign::documentation(), Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::documentation(), + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::documentation(), Self::EslintNoImportAssign(_) => EslintNoImportAssign::documentation(), Self::EslintNoInlineComments(_) => EslintNoInlineComments::documentation(), Self::EslintNoInnerDeclarations(_) => EslintNoInnerDeclarations::documentation(), @@ -7561,6 +7569,8 @@ impl RuleEnum { .or_else(|| EslintNoGlobalAssign::schema(generator)), Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::config_schema(generator) .or_else(|| EslintNoImplicitCoercion::schema(generator)), + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::config_schema(generator) + .or_else(|| EslintNoImplicitGlobals::schema(generator)), Self::EslintNoImportAssign(_) => EslintNoImportAssign::config_schema(generator) .or_else(|| EslintNoImportAssign::schema(generator)), Self::EslintNoInlineComments(_) => EslintNoInlineComments::config_schema(generator) @@ -9676,6 +9686,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => "eslint", Self::EslintNoGlobalAssign(_) => "eslint", Self::EslintNoImplicitCoercion(_) => "eslint", + Self::EslintNoImplicitGlobals(_) => "eslint", Self::EslintNoImportAssign(_) => "eslint", Self::EslintNoInlineComments(_) => "eslint", Self::EslintNoInnerDeclarations(_) => "eslint", @@ -10680,6 +10691,9 @@ impl RuleEnum { Self::EslintNoImplicitCoercion(_) => Ok(Self::EslintNoImplicitCoercion( EslintNoImplicitCoercion::from_configuration(value)?, )), + Self::EslintNoImplicitGlobals(_) => Ok(Self::EslintNoImplicitGlobals( + EslintNoImplicitGlobals::from_configuration(value)?, + )), Self::EslintNoImportAssign(_) => { Ok(Self::EslintNoImportAssign(EslintNoImportAssign::from_configuration(value)?)) } @@ -13017,6 +13031,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(rule) => rule.to_configuration(), Self::EslintNoGlobalAssign(rule) => rule.to_configuration(), Self::EslintNoImplicitCoercion(rule) => rule.to_configuration(), + Self::EslintNoImplicitGlobals(rule) => rule.to_configuration(), Self::EslintNoImportAssign(rule) => rule.to_configuration(), Self::EslintNoInlineComments(rule) => rule.to_configuration(), Self::EslintNoInnerDeclarations(rule) => rule.to_configuration(), @@ -13814,6 +13829,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(rule) => rule.run(node, ctx), Self::EslintNoGlobalAssign(rule) => rule.run(node, ctx), Self::EslintNoImplicitCoercion(rule) => rule.run(node, ctx), + Self::EslintNoImplicitGlobals(rule) => rule.run(node, ctx), Self::EslintNoImportAssign(rule) => rule.run(node, ctx), Self::EslintNoInlineComments(rule) => rule.run(node, ctx), Self::EslintNoInnerDeclarations(rule) => rule.run(node, ctx), @@ -14607,6 +14623,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(rule) => rule.run_once(ctx), Self::EslintNoGlobalAssign(rule) => rule.run_once(ctx), Self::EslintNoImplicitCoercion(rule) => rule.run_once(ctx), + Self::EslintNoImplicitGlobals(rule) => rule.run_once(ctx), Self::EslintNoImportAssign(rule) => rule.run_once(ctx), Self::EslintNoInlineComments(rule) => rule.run_once(ctx), Self::EslintNoInnerDeclarations(rule) => rule.run_once(ctx), @@ -15404,6 +15421,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(rule) => rule.run_on_jest_node(jest_node, ctx), Self::EslintNoGlobalAssign(rule) => rule.run_on_jest_node(jest_node, ctx), Self::EslintNoImplicitCoercion(rule) => rule.run_on_jest_node(jest_node, ctx), + Self::EslintNoImplicitGlobals(rule) => rule.run_on_jest_node(jest_node, ctx), Self::EslintNoImportAssign(rule) => rule.run_on_jest_node(jest_node, ctx), Self::EslintNoInlineComments(rule) => rule.run_on_jest_node(jest_node, ctx), Self::EslintNoInnerDeclarations(rule) => rule.run_on_jest_node(jest_node, ctx), @@ -16303,6 +16321,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(rule) => rule.should_run(ctx), Self::EslintNoGlobalAssign(rule) => rule.should_run(ctx), Self::EslintNoImplicitCoercion(rule) => rule.should_run(ctx), + Self::EslintNoImplicitGlobals(rule) => rule.should_run(ctx), Self::EslintNoImportAssign(rule) => rule.should_run(ctx), Self::EslintNoInlineComments(rule) => rule.should_run(ctx), Self::EslintNoInnerDeclarations(rule) => rule.should_run(ctx), @@ -17106,6 +17125,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => EslintNoFuncAssign::IS_TSGOLINT_RULE, Self::EslintNoGlobalAssign(_) => EslintNoGlobalAssign::IS_TSGOLINT_RULE, Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::IS_TSGOLINT_RULE, + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::IS_TSGOLINT_RULE, Self::EslintNoImportAssign(_) => EslintNoImportAssign::IS_TSGOLINT_RULE, Self::EslintNoInlineComments(_) => EslintNoInlineComments::IS_TSGOLINT_RULE, Self::EslintNoInnerDeclarations(_) => EslintNoInnerDeclarations::IS_TSGOLINT_RULE, @@ -18239,6 +18259,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => EslintNoFuncAssign::VERSION, Self::EslintNoGlobalAssign(_) => EslintNoGlobalAssign::VERSION, Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::VERSION, + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::VERSION, Self::EslintNoImportAssign(_) => EslintNoImportAssign::VERSION, Self::EslintNoInlineComments(_) => EslintNoInlineComments::VERSION, Self::EslintNoInnerDeclarations(_) => EslintNoInnerDeclarations::VERSION, @@ -19199,6 +19220,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(_) => EslintNoFuncAssign::HAS_CONFIG, Self::EslintNoGlobalAssign(_) => EslintNoGlobalAssign::HAS_CONFIG, Self::EslintNoImplicitCoercion(_) => EslintNoImplicitCoercion::HAS_CONFIG, + Self::EslintNoImplicitGlobals(_) => EslintNoImplicitGlobals::HAS_CONFIG, Self::EslintNoImportAssign(_) => EslintNoImportAssign::HAS_CONFIG, Self::EslintNoInlineComments(_) => EslintNoInlineComments::HAS_CONFIG, Self::EslintNoInnerDeclarations(_) => EslintNoInnerDeclarations::HAS_CONFIG, @@ -20184,6 +20206,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(rule) => rule.types_info(), Self::EslintNoGlobalAssign(rule) => rule.types_info(), Self::EslintNoImplicitCoercion(rule) => rule.types_info(), + Self::EslintNoImplicitGlobals(rule) => rule.types_info(), Self::EslintNoImportAssign(rule) => rule.types_info(), Self::EslintNoInlineComments(rule) => rule.types_info(), Self::EslintNoInnerDeclarations(rule) => rule.types_info(), @@ -20977,6 +21000,7 @@ impl RuleEnum { Self::EslintNoFuncAssign(rule) => rule.run_info(), Self::EslintNoGlobalAssign(rule) => rule.run_info(), Self::EslintNoImplicitCoercion(rule) => rule.run_info(), + Self::EslintNoImplicitGlobals(rule) => rule.run_info(), Self::EslintNoImportAssign(rule) => rule.run_info(), Self::EslintNoInlineComments(rule) => rule.run_info(), Self::EslintNoInnerDeclarations(rule) => rule.run_info(), @@ -21792,6 +21816,7 @@ pub static RULES: std::sync::LazyLock> = std::sync::LazyLock::new( RuleEnum::EslintNoFuncAssign(EslintNoFuncAssign::default()), RuleEnum::EslintNoGlobalAssign(EslintNoGlobalAssign::default()), RuleEnum::EslintNoImplicitCoercion(EslintNoImplicitCoercion::default()), + RuleEnum::EslintNoImplicitGlobals(EslintNoImplicitGlobals::default()), RuleEnum::EslintNoImportAssign(EslintNoImportAssign::default()), RuleEnum::EslintNoInlineComments(EslintNoInlineComments::default()), RuleEnum::EslintNoInnerDeclarations(EslintNoInnerDeclarations::default()), diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index dcb876723e7dd..a120f3061c470 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -113,6 +113,7 @@ pub(crate) mod eslint { pub mod no_func_assign; pub mod no_global_assign; pub mod no_implicit_coercion; + pub mod no_implicit_globals; pub mod no_import_assign; pub mod no_inline_comments; pub mod no_inner_declarations; diff --git a/crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs b/crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs new file mode 100644 index 0000000000000..e38dd037481a9 --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs @@ -0,0 +1,710 @@ +use javascript_globals::{GLOBALS, GLOBALS_BUILTIN}; +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::{GetSpan, Span}; +use oxc_syntax::{reference::Reference, symbol::SymbolFlags}; +use rustc_hash::{FxHashMap, FxHashSet}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{ + config::GlobalValue, + context::LintContext, + rule::{DefaultRuleConfig, Rule}, +}; + +#[derive(Debug, Clone, Copy)] +enum DiagnosticKind { + GlobalNonLexicalBinding(&'static str), + GlobalLexicalBinding(&'static str), + GlobalVariableLeak, + AssignmentToReadonlyGlobal, + RedeclarationOfReadonlyGlobal, +} + +fn no_implicit_globals_diagnostic(kind: DiagnosticKind, span: Span) -> OxcDiagnostic { + match kind { + DiagnosticKind::GlobalNonLexicalBinding(kind) => OxcDiagnostic::warn(format!( + "Unexpected {kind} declaration in the global scope." + )) + .with_help( + "Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable.", + ) + .with_label(span), + DiagnosticKind::GlobalLexicalBinding(kind) => OxcDiagnostic::warn(format!( + "Unexpected {kind} declaration in the global scope." + )) + .with_help("Wrap it in a block or in an IIFE.") + .with_label(span), + DiagnosticKind::GlobalVariableLeak => OxcDiagnostic::warn("Global variable leak.") + .with_help("Declare the variable if it is intended to be local.") + .with_label(span), + DiagnosticKind::AssignmentToReadonlyGlobal => { + OxcDiagnostic::warn("Unexpected assignment to read-only global variable.") + .with_label(span) + } + DiagnosticKind::RedeclarationOfReadonlyGlobal => { + OxcDiagnostic::warn("Unexpected redeclaration of read-only global variable.") + .with_label(span) + } + } +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase", default, deny_unknown_fields)] +struct NoImplicitGlobalsConfig { + lexical_bindings: bool, +} + +#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema)] +pub struct NoImplicitGlobals(NoImplicitGlobalsConfig); + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallows declarations in the global scope, global variable leaks, and + /// writes or redeclarations of read-only globals. + /// + /// ### Why is this bad? + /// + /// Browser scripts share a global scope. Top-level `var` and `function` + /// declarations, and assignments to undeclared variables in sloppy mode, + /// create globals that can collide with other scripts. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// var foo = 1; + /// function bar() {} + /// baz = 1; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// window.foo = 1; + /// (function() { + /// var bar = 1; + /// }()); + /// ``` + NoImplicitGlobals, + eslint, + restriction, + config = NoImplicitGlobals, + version = "next", +); + +impl Rule for NoImplicitGlobals { + fn from_configuration(value: serde_json::Value) -> Result { + serde_json::from_value::>(value).map(DefaultRuleConfig::into_inner) + } + + fn run_once(&self, ctx: &LintContext) { + let comment_config = CommentConfig::new(ctx); + let scoping = ctx.scoping(); + let root_scope_id = scoping.root_scope_id(); + let check_global_declarations = + !ctx.source_type().is_module() && !ctx.source_type().is_commonjs(); + + if check_global_declarations { + for symbol_id in scoping.iter_bindings_in(root_scope_id) { + let name = scoping.symbol_name(symbol_id); + + if comment_config.exported.contains(name) { + continue; + } + + let global_value = global_variable_value(name, ctx, &comment_config.globals); + if global_value == Some(GlobalValue::Writable) { + continue; + } + + let declarations = scoping.symbol_redeclarations(symbol_id); + if declarations.is_empty() { + check_declaration( + scoping.symbol_flags(symbol_id), + scoping.symbol_span(symbol_id), + global_value, + self.0.lexical_bindings, + ctx, + ); + } else { + for redeclaration in declarations { + check_declaration( + redeclaration.flags, + redeclaration.span, + global_value, + self.0.lexical_bindings, + ctx, + ); + } + } + } + } + + for (name, reference_id_list) in scoping.root_unresolved_references() { + let global_value = global_variable_value(name, ctx, &comment_config.globals); + + if global_value == Some(GlobalValue::Writable) { + continue; + } + + for &reference_id in reference_id_list { + let reference = scoping.get_reference(reference_id); + if reference.is_type() || !reference.flags().is_write_only() { + continue; + } + + if global_value == Some(GlobalValue::Readonly) { + ctx.diagnostic(no_implicit_globals_diagnostic( + DiagnosticKind::AssignmentToReadonlyGlobal, + assignment_span(reference, ctx), + )); + } else if !scoping.scope_flags(reference.scope_id()).is_strict_mode() { + ctx.diagnostic(no_implicit_globals_diagnostic( + DiagnosticKind::GlobalVariableLeak, + assignment_span(reference, ctx), + )); + } + } + } + } +} + +#[derive(Debug, Default)] +struct CommentConfig { + globals: FxHashMap, + exported: FxHashSet, +} + +impl CommentConfig { + fn new(ctx: &LintContext) -> Self { + let mut config = Self::default(); + + for comment in ctx.semantic().comments() { + let content = ctx.source_range(comment.content_span()).trim(); + let Some((directive, rest)) = content.split_once(char::is_whitespace) else { + continue; + }; + let rest = rest.trim(); + + match directive { + "global" | "globals" => parse_global_comment(rest, &mut config.globals), + "exported" => parse_exported_comment(rest, &mut config.exported), + _ => {} + } + } + + config + } +} + +fn parse_global_comment(rest: &str, globals: &mut FxHashMap) { + for item in rest.split(',') { + let item = item.trim(); + if item.is_empty() { + continue; + } + + let (name, value) = item + .split_once(':') + .map_or((item, None), |(name, value)| (name.trim(), Some(value.trim()))); + + if name.is_empty() { + continue; + } + + let value = match value { + Some("writable" | "writeable" | "true") => GlobalValue::Writable, + Some("off") => GlobalValue::Off, + Some("readonly" | "readable" | "false") | None => GlobalValue::Readonly, + Some(_) => continue, + }; + + globals.insert(name.to_string(), value); + } +} + +fn parse_exported_comment(rest: &str, exported: &mut FxHashSet) { + for name in rest.split(|c: char| c == ',' || c.is_whitespace()) { + let name = name.trim(); + if !name.is_empty() { + exported.insert(name.to_string()); + } + } +} + +fn global_variable_value( + name: &str, + ctx: &LintContext, + comment_globals: &FxHashMap, +) -> Option { + if let Some(value) = comment_globals.get(name) { + return Some(*value); + } + + if let Some(value) = ctx.globals().get(name) { + return Some(*value); + } + + if GLOBALS_BUILTIN.contains_key(name) { + return Some(GlobalValue::Readonly); + } + + for env in ctx.env().iter() { + if let Some(env) = GLOBALS.get(env) + && let Some(value) = env.get(name) + { + return Some(GlobalValue::from(*value)); + } + } + + None +} + +fn check_declaration( + flags: SymbolFlags, + span: Span, + global_value: Option, + check_lexical_bindings: bool, + ctx: &LintContext, +) { + let Some(kind) = declaration_kind(flags, check_lexical_bindings) else { + return; + }; + + if global_value == Some(GlobalValue::Readonly) { + ctx.diagnostic(no_implicit_globals_diagnostic( + DiagnosticKind::RedeclarationOfReadonlyGlobal, + span, + )); + return; + } + + let diagnostic_kind = match kind { + DeclarationKind::Function | DeclarationKind::Var => { + DiagnosticKind::GlobalNonLexicalBinding(kind.as_message_kind()) + } + DeclarationKind::Class | DeclarationKind::Let | DeclarationKind::Const => { + DiagnosticKind::GlobalLexicalBinding(kind.as_message_kind()) + } + }; + + ctx.diagnostic(no_implicit_globals_diagnostic(diagnostic_kind, span)); +} + +#[derive(Debug, Clone, Copy)] +enum DeclarationKind { + Var, + Function, + Let, + Const, + Class, +} + +impl DeclarationKind { + fn as_message_kind(self) -> &'static str { + match self { + Self::Var => "'var'", + Self::Function => "function", + Self::Let => "'let'", + Self::Const => "'const'", + Self::Class => "class", + } + } +} + +fn declaration_kind(flags: SymbolFlags, check_lexical_bindings: bool) -> Option { + if flags.is_function() { + return Some(DeclarationKind::Function); + } + + if flags.contains(SymbolFlags::FunctionScopedVariable) { + return Some(DeclarationKind::Var); + } + + if check_lexical_bindings { + if flags.is_class() { + return Some(DeclarationKind::Class); + } + if flags.contains(SymbolFlags::BlockScopedVariable) { + return Some(if flags.is_const_variable() { + DeclarationKind::Const + } else { + DeclarationKind::Let + }); + } + } + + None +} + +fn assignment_span(reference: &Reference, ctx: &LintContext) -> Span { + let reference_node = ctx.nodes().get_node(reference.node_id()); + let mut node = reference_node; + + loop { + let parent = ctx.nodes().parent_node(node.id()); + match parent.kind() { + AstKind::AssignmentExpression(_) + | AstKind::ForInStatement(_) + | AstKind::ForOfStatement(_) => return parent.kind().span(), + _ if parent.id() == node.id() => return reference_node.kind().span(), + _ => node = parent, + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("window.foo = 1;", None), // { "globals": globals.browser }, + ("window.foo = function() {};", None), // { "globals": globals.browser }, + ("window.foo = function foo() {};", None), // { "globals": globals.browser }, + ("window.foo = function bar() {};", None), // { "globals": globals.browser }, + ("window.foo = function*() {};", None), // { "ecmaVersion": 2015, "globals": globals.browser, }, + ("window.foo = function *foo() {};", None), // { "ecmaVersion": 2015, "globals": globals.browser, }, + ("window.foo = async function() {};", None), // { "ecmaVersion": 2017, "globals": globals.browser, }, + ("window.foo = async function foo() {};", None), // { "ecmaVersion": 2017, "globals": globals.browser, }, + ("window.foo = async function*() {};", None), // { "ecmaVersion": 2018, "globals": globals.browser, }, + ("window.foo = async function *foo() {};", None), // { "ecmaVersion": 2018, "globals": globals.browser, }, + ("window.foo = class {};", None), // { "ecmaVersion": 2015, "globals": globals.browser, }, + ("window.foo = class foo {};", None), // { "ecmaVersion": 2015, "globals": globals.browser, }, + ("window.foo = class bar {};", None), // { "ecmaVersion": 2015, "globals": globals.browser, }, + ("self.foo = 1;", None), // { "globals": globals.browser }, + ("self.foo = function() {};", None), // { "globals": globals.browser }, + ("this.foo = 1;", None), + ("this.foo = function() {};", None), + ("this.foo = function bar() {};", None), + ("/*global foo:readonly*/", None), + ("/*global foo:writable*/", None), + ("/*global Array:readonly*/", None), + ("/*global Array:writable*/", None), + ("/*global foo:readonly*/", None), // { "globals": { "foo": "readonly" } }, + ("/*global foo:writable*/", None), // { "globals": { "foo": "readonly" } }, + ("/*global foo:readonly*/", None), // { "globals": { "foo": "writable" } }, + ("/*global foo:writable*/", None), // { "globals": { "foo": "writable" } }, + ("typeof function() {}", None), + ("typeof function foo() {}", None), + ("(function() {}) + (function foo() {})", None), + ("typeof function *foo() {}", None), // { "ecmaVersion": 2015 }, + ("typeof async function foo() {}", None), // { "ecmaVersion": 2017 }, + ("typeof async function *foo() {}", None), // { "ecmaVersion": 2018 }, + ("(function() { var foo = 1; })();", None), + ("(function() { function foo() {} })();", None), + ("(function() { function *foo() {} })();", None), // { "ecmaVersion": 2015 }, + ("(function() { async function foo() {} })();", None), // { "ecmaVersion": 2017 }, + ("(function() { async function *foo() {} })();", None), // { "ecmaVersion": 2018 }, + ( + "window.foo = (function() { var bar; function foo () {}; return function bar() {} })();", + None, + ), // { "globals": globals.browser }, + ("const foo = 1; let bar; class Baz {}", None), // { "ecmaVersion": 2015 }, + ( + "const foo = 1; let bar; class Baz {}", + Some(serde_json::json!([{ "lexicalBindings": false }])), + ), // { "ecmaVersion": 2015 }, + ("const Array = 1; let Object; class Math {}", None), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly, bar:readonly, Baz:readonly*/ const foo = 1; let bar; class Baz {}", + None, + ), // { "ecmaVersion": 2015 }, + ("typeof class {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("typeof class foo {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ( + "{ const foo = 1; let bar; class Baz {} }", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "(function() { const foo = 1; let bar; class Baz {} })();", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "window.foo = (function() { const bar = 1; let baz; class Quux {} return function () {} })();", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("const foo = 1; let bar; class Baz {}", None), // { "ecmaVersion": 2015, "sourceType": "module" }, + ("const foo = 1; let bar; class Baz {}", None), // { "ecmaVersion": 2015, "sourceType": "commonjs", }, + ("const foo = 1; let bar; class Baz {}", None), // { "ecmaVersion": 2015, "parserOptions": { "ecmaFeatures": { "globalReturn": true } }, }, + ("const foo = 1;", None), // { "ecmaVersion": 2015 }, + ("let foo = 1;", None), // { "ecmaVersion": 2015 }, + ("let foo = function() {};", None), // { "ecmaVersion": 2015 }, + ("const foo = function() {};", None), // { "ecmaVersion": 2015 }, + ("class Foo {}", None), // { "ecmaVersion": 2015 }, + ("(function() { let foo = 1; })();", None), // { "ecmaVersion": 2015 }, + ("(function() { const foo = 1; })();", None), // { "ecmaVersion": 2015 }, + ("let foo = 1;", None), // { "ecmaVersion": 2015, "sourceType": "module" }, + ("const foo = 1;", None), // { "ecmaVersion": 2015, "sourceType": "module" }, + ("let foo = 1;", None), // { "ecmaVersion": 2015, "parserOptions": { "ecmaFeatures": { "globalReturn": true } }, }, + ("const foo = 1;", None), // { "ecmaVersion": 2015, "parserOptions": { "ecmaFeatures": { "globalReturn": true } }, }, + ("foo", None), + ("foo + bar", None), + ("foo(bar)", None), + ("foo++", None), + ("--foo", None), + ("foo += 1", None), + ("foo ||= 1", None), // { "ecmaVersion": 2021 }, + ("/* global foo: writable*/ foo = bar", None), + ("'use strict';foo = 1;", None), + ("(function() {'use strict'; foo = 1; })();", None), + ("{ class Foo { constructor() { bar = 1; } baz() { bar = 1; } } }", None), // { "ecmaVersion": 2015 }, + ("Foo.bar = 1;", None), + ("Utils.foo = 1;", None), + ("Utils.foo = function() {};", None), + ("window.foo = 1;", None), + ("window.foo = function() {};", None), + ("window.foo = function foo() {};", None), + ("self.foo = 1;", None), + ("self.foo = function() {};", None), + ("++foo", None), + ("foo--", None), + ("window.foo = function bar() { bar = 1; };", None), // { "globals": globals.browser }, + ("window.foo = function bar(baz) { baz = 1; };", None), // { "globals": globals.browser }, + ("window.foo = function bar() { var baz; function quux() { quux = 1; } };", None), // { "globals": globals.browser }, + ("/*global foo:writable*/ var foo = 1;", None), + ("/*global foo:writable*/ function *foo() {}", None), // { "ecmaVersion": 2015 }, + ( + "/*global foo:writable*/ const foo = 1;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:writable*/ let foo;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global Foo:writable*/ class Foo {}", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("/*global foo:writable*/ foo = 1;", None), + ("Array.from = 1;", None), + ("Object['assign'] = 1;", None), + ("/*global foo:readonly*/ foo.bar = 1;", None), + ("/*global foo:readonly*/ foo++;", None), + ("/*global foo:readonly*/ --foo;", None), + ("/*global foo:readonly*/ foo += 1;", None), + ("/*global foo:readonly*/ foo ||= 1;", None), // { "ecmaVersion": 2021 }, + ("/* exported foo */ var foo = 'foo';", None), + ("/* exported foo */ function foo() {}", None), + ("/* exported foo */ function *foo() {}", None), // { "ecmaVersion": 2015 }, + ("/* exported foo */ async function foo() {}", None), // { "ecmaVersion": 2017 }, + ("/* exported foo */ async function *foo() {}", None), // { "ecmaVersion": 2018 }, + ("/* exported foo */ var foo = function() {};", None), + ("/* exported foo */ var foo = function foo() {};", None), + ("/* exported foo */ var foo = function*() {};", None), // { "ecmaVersion": 2015 }, + ("/* exported foo */ var foo = function *foo() {};", None), // { "ecmaVersion": 2015 }, + ("/* exported foo, bar */ var foo = 1, bar = 2;", None), + ("/* exported a */ const a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("/* exported a */ let a;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("/* exported a */ let a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("/* exported A */ class A {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ( + "/* exported a, b */ const a = 1; const b = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a, b */ const a = 1, b = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a, b */ let a, b = 1;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a, b, C */ const a = 1; let b; class C {}", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a, b, c */ const [a, b, ...c] = [];", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a, b, c */ let { a, foo: b, bar: { c } } = {};", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 } + ]; + + let fail = vec![ + ("var foo = 1;", None), + ("function foo() {}", None), + ("function *foo() {}", None), // { "ecmaVersion": 2015 }, + ("async function foo() {}", None), // { "ecmaVersion": 2017 }, + ("async function *foo() {}", None), // { "ecmaVersion": 2018 }, + ("var foo = function() {};", None), + ("var foo = function foo() {};", None), + ("var foo = function*() {};", None), // { "ecmaVersion": 2015 }, + ("var foo = function *foo() {};", None), // { "ecmaVersion": 2015 }, + ("var foo = 1, bar = 2;", None), + ("const a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("let a;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("let a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("class A {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("const a = 1; const b = 2;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("const a = 1, b = 2;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("let a, b = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("const a = 1; let b; class C {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("const [a, b, ...c] = [];", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ( + "let { a, foo: b, bar: { c } } = {};", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("foo = 1", None), + ("foo = function() {};", None), + ("foo = function*() {};", None), // { "ecmaVersion": 2015 }, + ("window.foo = function() { bar = 1; }", None), + ("(function() {}(foo = 1));", None), + ("for (foo in {});", None), + ("for (foo of []);", None), // { "ecmaVersion": 2015 }, + ("window.foo = { bar() { foo = 1 } }", None), // { "ecmaVersion": 2015 }, + ("foo = 1, bar = 2;", None), + ("foo = bar = 1", None), + ("/*global foo:writable*/ foo = bar = 1", None), + ("/*global bar:writable*/ foo = bar = 1", None), + ("foo = 1; var bar;", None), + ("var foo = bar = 1;", None), + ("/*global foo:writable*/ var foo = bar = 1;", None), + ("/*global bar:writable*/ var foo = bar = 1;", None), + ("[foo, bar] = [];", None), // { "ecmaVersion": 2015 }, + ("/*global foo:writable*/ [foo, bar] = [];", None), // { "ecmaVersion": 2015 }, + ("/*global bar:writable*/ [foo, bar] = [];", None), // { "ecmaVersion": 2015 }, + ("Array = 1", None), + ("window = 1;", None), // { "globals": globals.browser }, + ("/*global foo:readonly*/ foo = 1", None), + ("foo = 1;", None), // { "globals": { "foo": "readonly" } }, + ("/*global foo:readonly*/ for (foo in {});", None), + ("/*global foo:readonly*/ for (foo of []);", None), // { "ecmaVersion": 2015 }, + ("var Array = 1", None), + ("var Array = 1; Array = 2;", None), + ("/*global foo:readonly*/ var foo", None), + ("/*global foo:readonly*/ var foo = 1", None), + ("/*global foo:readonly*/ var foo; foo = 1;", None), + ("/*global foo:readonly*/ for (var foo in obj);", None), + ("/*global foo:readonly*/ for (var foo in obj); foo = 1;", None), + ("/*global foo:readonly*/ for (var foo of arr);", None), // { "ecmaVersion": 2015 }, + ("/*global foo:readonly*/ for (var foo of arr); foo = 1;", None), // { "ecmaVersion": 2015 }, + ("/*global foo:readonly*/ function foo() {}", None), + ( + "/*global foo:readonly*/ const foo = 1", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly*/ const foo = 1; foo = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("/*global foo:readonly*/ let foo", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly*/ let foo = 1", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly*/ let foo; foo = 1;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global Foo:readonly*/ class Foo {}", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("/*global foo:readonly, bar: readonly*/ foo = bar = 1", None), + ("/*global foo:writable, bar: readonly*/ foo = bar = 1", None), + ("/*global foo:readonly, bar: writable*/ foo = bar = 1", None), + ("/*global foo: readonly*/ foo = bar = 1", None), + ("/*global bar: readonly*/ foo = bar = 1", None), + ("/*global foo*/ [foo] = arr", None), // { "ecmaVersion": 2015 }, + ("/*global foo, bar: readonly*/ [foo, bar] = arr", None), // { "ecmaVersion": 2015 }, + ("/*global foo: readonly*/ ({ foo } = obj)", None), // { "ecmaVersion": 2015 }, + ("/*global foo: readonly*/ ({ 'a': foo } = obj)", None), // { "ecmaVersion": 2015 }, + ("/*global foo: readonly*/ ({ 'a': { 'b': [foo] } } = obj)", None), // { "ecmaVersion": 2015 }, + ("/*global foo, bar: readonly*/ ({ foo, 'a': bar } = obj)", None), // { "ecmaVersion": 2015 }, + ("/*global foo:readonly, bar: readonly*/ var foo, bar;", None), + ("/*global foo:writable, bar: readonly*/ var foo, bar;", None), + ("/*global foo:readonly, bar: writable*/ var foo, bar;", None), + ("/*global foo:readonly*/ var foo, bar;", None), + ("/*global bar: readonly*/ var foo, bar;", None), + ( + "/*global foo:readonly, bar: readonly*/ const foo = 1, bar = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:writable, bar: readonly*/ const foo = 1, bar = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly, bar: writable*/ const foo = 1, bar = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly*/ const foo = 1, bar = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global bar: readonly*/ const foo = 1, bar = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly, bar: readonly*/ let foo, bar;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:writable, bar: readonly*/ let foo, bar;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly, bar: writable*/ let foo, bar;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global foo:readonly*/ let foo, bar;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/*global bar: readonly*/ let foo, bar;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("/* exported bar */ var foo = 'text';", None), + ("/* exported bar */ function foo() {}", None), + ("/* exported bar */ function *foo() {}", None), // { "ecmaVersion": 2015 }, + ("/* exported bar */ async function foo() {}", None), // { "ecmaVersion": 2017 }, + ("/* exported bar */ async function *foo() {}", None), // { "ecmaVersion": 2018 }, + ("/* exported bar */ var foo = function() {};", None), + ("/* exported bar */ var foo = function foo() {};", None), + ("/* exported bar */ var foo = function*() {};", None), // { "ecmaVersion": 2015 }, + ("/* exported bar */ var foo = function *foo() {};", None), // { "ecmaVersion": 2015 }, + ("/* exported bar */ var foo = 1, bar = 2;", None), + ("/* exported b */ const a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("/* exported b */ let a;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("/* exported b */ let a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ("/* exported B */ class A {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ( + "/* exported a */ const a = 1; const b = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a */ const a = 1, b = 2;", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("/* exported a */ let a, b = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, + ( + "/* exported a */ const a = 1; let b; class C {}", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a */ const [a, b, ...c] = [];", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "/* exported a */ let { a, foo: b, bar: { c } } = {};", + Some(serde_json::json!([{ "lexicalBindings": true }])), + ), // { "ecmaVersion": 2015 }, + ("/* exported foo */ foo = 1", None), + ("/* exported foo */ foo = function() {};", None), + ("/* exported foo */ foo = function*() {};", None), // { "ecmaVersion": 2015 }, + ("/* exported foo */ window.foo = function() { bar = 1; }", None), + ("/* exported foo */ (function() {}(foo = 1));", None), + ("/* exported foo */ for (foo in {});", None), + ("/* exported foo */ for (foo of []);", None), // { "ecmaVersion": 2015 } + ]; + + Tester::new(NoImplicitGlobals::NAME, NoImplicitGlobals::PLUGIN, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap b/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap new file mode 100644 index 0000000000000..0ba8485235614 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap @@ -0,0 +1,973 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = 1; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:10] + 1 │ function foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:11] + 1 │ function *foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:16] + 1 │ async function foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:17] + 1 │ async function *foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = function() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = function foo() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = function*() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = function *foo() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:14] + 1 │ var foo = 1, bar = 2; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = 1, bar = 2; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ const a = 1; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ let a; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ let a = 1; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected class declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ class A {} + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:20] + 1 │ const a = 1; const b = 2; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ const a = 1; const b = 2; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:14] + 1 │ const a = 1, b = 2; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ const a = 1, b = 2; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:8] + 1 │ let a, b = 1; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ let a, b = 1; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected class declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:27] + 1 │ const a = 1; let b; class C {} + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:18] + 1 │ const a = 1; let b; class C {} + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ const a = 1; let b; class C {} + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:17] + 1 │ const [a, b, ...c] = []; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:11] + 1 │ const [a, b, ...c] = []; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:8] + 1 │ const [a, b, ...c] = []; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ let { a, foo: b, bar: { c } } = {}; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:15] + 1 │ let { a, foo: b, bar: { c } } = {}; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ let { a, foo: b, bar: { c } } = {}; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = 1 + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = function() {}; + · ─────────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = function*() {}; + · ──────────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:27] + 1 │ window.foo = function() { bar = 1; } + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:16] + 1 │ (function() {}(foo = 1)); + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ for (foo in {}); + · ──────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ for (foo of []); + · ──────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ window.foo = { bar() { foo = 1 } } + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:10] + 1 │ foo = 1, bar = 2; + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = 1, bar = 2; + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ foo = bar = 1 + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = bar = 1 + · ───────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /*global foo:writable*/ foo = bar = 1 + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ /*global bar:writable*/ foo = bar = 1 + · ───────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:14] + 1 │ foo = 1; var bar; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = 1; var bar; + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = bar = 1; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:11] + 1 │ var foo = bar = 1; + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:35] + 1 │ /*global foo:writable*/ var foo = bar = 1; + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global bar:writable*/ var foo = bar = 1; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ [foo, bar] = []; + · ─────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ [foo, bar] = []; + · ─────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ /*global foo:writable*/ [foo, bar] = []; + · ─────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ /*global bar:writable*/ [foo, bar] = []; + · ─────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ Array = 1 + · ───────── + ╰──── + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ window = 1; + · ────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ /*global foo:readonly*/ foo = 1 + · ─────── + ╰──── + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = 1; + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ /*global foo:readonly*/ for (foo in {}); + · ──────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ /*global foo:readonly*/ for (foo of []); + · ──────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var Array = 1 + · ───── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var Array = 1; Array = 2; + · ───── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ var foo + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ var foo = 1 + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ var foo; foo = 1; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /*global foo:readonly*/ for (var foo in obj); + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /*global foo:readonly*/ for (var foo in obj); foo = 1; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /*global foo:readonly*/ for (var foo of arr); + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /*global foo:readonly*/ for (var foo of arr); foo = 1; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /*global foo:readonly*/ function foo() {} + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /*global foo:readonly*/ const foo = 1 + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /*global foo:readonly*/ const foo = 1; foo = 2; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ let foo + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ let foo = 1 + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ let foo; foo = 1; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /*global Foo:readonly*/ class Foo {} + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:46] + 1 │ /*global foo:readonly, bar: readonly*/ foo = bar = 1 + · ─────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:40] + 1 │ /*global foo:readonly, bar: readonly*/ foo = bar = 1 + · ───────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:46] + 1 │ /*global foo:writable, bar: readonly*/ foo = bar = 1 + · ─────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:40] + 1 │ /*global foo:readonly, bar: writable*/ foo = bar = 1 + · ───────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:32] + 1 │ /*global foo: readonly*/ foo = bar = 1 + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:26] + 1 │ /*global foo: readonly*/ foo = bar = 1 + · ───────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:32] + 1 │ /*global bar: readonly*/ foo = bar = 1 + · ─────── + ╰──── + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:26] + 1 │ /*global bar: readonly*/ foo = bar = 1 + · ───────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:16] + 1 │ /*global foo*/ [foo] = arr + · ─────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /*global foo, bar: readonly*/ [foo, bar] = arr + · ──────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /*global foo, bar: readonly*/ [foo, bar] = arr + · ──────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:27] + 1 │ /*global foo: readonly*/ ({ foo } = obj) + · ───────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:27] + 1 │ /*global foo: readonly*/ ({ 'a': foo } = obj) + · ────────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:27] + 1 │ /*global foo: readonly*/ ({ 'a': { 'b': [foo] } } = obj) + · ───────────────────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:32] + 1 │ /*global foo, bar: readonly*/ ({ foo, 'a': bar } = obj) + · ─────────────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:32] + 1 │ /*global foo, bar: readonly*/ ({ foo, 'a': bar } = obj) + · ─────────────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:49] + 1 │ /*global foo:readonly, bar: readonly*/ var foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:44] + 1 │ /*global foo:readonly, bar: readonly*/ var foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:49] + 1 │ /*global foo:writable, bar: readonly*/ var foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:44] + 1 │ /*global foo:readonly, bar: writable*/ var foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /*global foo:readonly*/ var foo, bar; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ var foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:35] + 1 │ /*global bar: readonly*/ var foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:30] + 1 │ /*global bar: readonly*/ var foo, bar; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:55] + 1 │ /*global foo:readonly, bar: readonly*/ const foo = 1, bar = 2; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:46] + 1 │ /*global foo:readonly, bar: readonly*/ const foo = 1, bar = 2; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:55] + 1 │ /*global foo:writable, bar: readonly*/ const foo = 1, bar = 2; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:46] + 1 │ /*global foo:readonly, bar: writable*/ const foo = 1, bar = 2; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:40] + 1 │ /*global foo:readonly*/ const foo = 1, bar = 2; + · ─── + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /*global foo:readonly*/ const foo = 1, bar = 2; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:41] + 1 │ /*global bar: readonly*/ const foo = 1, bar = 2; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:32] + 1 │ /*global bar: readonly*/ const foo = 1, bar = 2; + · ─── + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:49] + 1 │ /*global foo:readonly, bar: readonly*/ let foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:44] + 1 │ /*global foo:readonly, bar: readonly*/ let foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:49] + 1 │ /*global foo:writable, bar: readonly*/ let foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:44] + 1 │ /*global foo:readonly, bar: writable*/ let foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /*global foo:readonly*/ let foo, bar; + · ─── + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /*global foo:readonly*/ let foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:35] + 1 │ /*global bar: readonly*/ let foo, bar; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:30] + 1 │ /*global bar: readonly*/ let foo, bar; + · ─── + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported bar */ var foo = 'text'; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:29] + 1 │ /* exported bar */ function foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:30] + 1 │ /* exported bar */ function *foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:35] + 1 │ /* exported bar */ async function foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:36] + 1 │ /* exported bar */ async function *foo() {} + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported bar */ var foo = function() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported bar */ var foo = function foo() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported bar */ var foo = function*() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported bar */ var foo = function *foo() {}; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported bar */ var foo = 1, bar = 2; + · ─── + ╰──── + help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported b */ const a = 1; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:22] + 1 │ /* exported b */ let a; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:22] + 1 │ /* exported b */ let a = 1; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected class declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:24] + 1 │ /* exported B */ class A {} + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:37] + 1 │ /* exported a */ const a = 1; const b = 2; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:31] + 1 │ /* exported a */ const a = 1, b = 2; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:25] + 1 │ /* exported a */ let a, b = 1; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected class declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:44] + 1 │ /* exported a */ const a = 1; let b; class C {} + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:35] + 1 │ /* exported a */ const a = 1; let b; class C {} + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:34] + 1 │ /* exported a */ const [a, b, ...c] = []; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:28] + 1 │ /* exported a */ const [a, b, ...c] = []; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:42] + 1 │ /* exported a */ let { a, foo: b, bar: { c } } = {}; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. + ╭─[no_implicit_globals.tsx:1:32] + 1 │ /* exported a */ let { a, foo: b, bar: { c } } = {}; + · ─ + ╰──── + help: Wrap it in a block or in an IIFE. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:20] + 1 │ /* exported foo */ foo = 1 + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:20] + 1 │ /* exported foo */ foo = function() {}; + · ─────────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:20] + 1 │ /* exported foo */ foo = function*() {}; + · ──────────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:46] + 1 │ /* exported foo */ window.foo = function() { bar = 1; } + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:35] + 1 │ /* exported foo */ (function() {}(foo = 1)); + · ─────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:20] + 1 │ /* exported foo */ for (foo in {}); + · ──────────────── + ╰──── + help: Declare the variable if it is intended to be local. + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:20] + 1 │ /* exported foo */ for (foo of []); + · ──────────────── + ╰──── + help: Declare the variable if it is intended to be local. From b79c2d40ee046a528e1f0ad2ae1b407da8bc229d Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Sun, 10 May 2026 07:36:19 +0200 Subject: [PATCH 2/2] fix(linter): address no-implicit-globals review --- .../src/rules/eslint/no_implicit_globals.rs | 415 +++--------- .../snapshots/eslint_no_implicit_globals.snap | 609 ------------------ ...o_implicit_globals@configured-globals.snap | 40 ++ 3 files changed, 123 insertions(+), 941 deletions(-) create mode 100644 crates/oxc_linter/src/snapshots/eslint_no_implicit_globals@configured-globals.snap diff --git a/crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs b/crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs index e38dd037481a9..a912b14f67c4f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs +++ b/crates/oxc_linter/src/rules/eslint/no_implicit_globals.rs @@ -4,7 +4,6 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use oxc_syntax::{reference::Reference, symbol::SymbolFlags}; -use rustc_hash::{FxHashMap, FxHashSet}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -14,41 +13,32 @@ use crate::{ rule::{DefaultRuleConfig, Rule}, }; -#[derive(Debug, Clone, Copy)] -enum DiagnosticKind { - GlobalNonLexicalBinding(&'static str), - GlobalLexicalBinding(&'static str), - GlobalVariableLeak, - AssignmentToReadonlyGlobal, - RedeclarationOfReadonlyGlobal, -} - -fn no_implicit_globals_diagnostic(kind: DiagnosticKind, span: Span) -> OxcDiagnostic { - match kind { - DiagnosticKind::GlobalNonLexicalBinding(kind) => OxcDiagnostic::warn(format!( - "Unexpected {kind} declaration in the global scope." - )) +fn global_non_lexical_binding_diagnostic(kind: &'static str, span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Unexpected {kind} declaration in the global scope.")) .with_help( "Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable.", ) - .with_label(span), - DiagnosticKind::GlobalLexicalBinding(kind) => OxcDiagnostic::warn(format!( - "Unexpected {kind} declaration in the global scope." - )) + .with_label(span) +} + +fn global_lexical_binding_diagnostic(kind: &'static str, span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Unexpected {kind} declaration in the global scope.")) .with_help("Wrap it in a block or in an IIFE.") - .with_label(span), - DiagnosticKind::GlobalVariableLeak => OxcDiagnostic::warn("Global variable leak.") - .with_help("Declare the variable if it is intended to be local.") - .with_label(span), - DiagnosticKind::AssignmentToReadonlyGlobal => { - OxcDiagnostic::warn("Unexpected assignment to read-only global variable.") - .with_label(span) - } - DiagnosticKind::RedeclarationOfReadonlyGlobal => { - OxcDiagnostic::warn("Unexpected redeclaration of read-only global variable.") - .with_label(span) - } - } + .with_label(span) +} + +fn global_variable_leak_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Global variable leak.") + .with_help("Declare the variable if it is intended to be local.") + .with_label(span) +} + +fn assignment_to_readonly_global_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Unexpected assignment to read-only global variable.").with_label(span) +} + +fn redeclaration_of_readonly_global_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Unexpected redeclaration of read-only global variable.").with_label(span) } #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] @@ -101,7 +91,6 @@ impl Rule for NoImplicitGlobals { } fn run_once(&self, ctx: &LintContext) { - let comment_config = CommentConfig::new(ctx); let scoping = ctx.scoping(); let root_scope_id = scoping.root_scope_id(); let check_global_declarations = @@ -111,11 +100,7 @@ impl Rule for NoImplicitGlobals { for symbol_id in scoping.iter_bindings_in(root_scope_id) { let name = scoping.symbol_name(symbol_id); - if comment_config.exported.contains(name) { - continue; - } - - let global_value = global_variable_value(name, ctx, &comment_config.globals); + let global_value = global_variable_value(name, ctx); if global_value == Some(GlobalValue::Writable) { continue; } @@ -144,7 +129,7 @@ impl Rule for NoImplicitGlobals { } for (name, reference_id_list) in scoping.root_unresolved_references() { - let global_value = global_variable_value(name, ctx, &comment_config.globals); + let global_value = global_variable_value(name, ctx); if global_value == Some(GlobalValue::Writable) { continue; @@ -157,93 +142,20 @@ impl Rule for NoImplicitGlobals { } if global_value == Some(GlobalValue::Readonly) { - ctx.diagnostic(no_implicit_globals_diagnostic( - DiagnosticKind::AssignmentToReadonlyGlobal, - assignment_span(reference, ctx), - )); + ctx.diagnostic(assignment_to_readonly_global_diagnostic(assignment_span( + reference, ctx, + ))); } else if !scoping.scope_flags(reference.scope_id()).is_strict_mode() { - ctx.diagnostic(no_implicit_globals_diagnostic( - DiagnosticKind::GlobalVariableLeak, - assignment_span(reference, ctx), - )); + ctx.diagnostic(global_variable_leak_diagnostic(assignment_span( + reference, ctx, + ))); } } } } } -#[derive(Debug, Default)] -struct CommentConfig { - globals: FxHashMap, - exported: FxHashSet, -} - -impl CommentConfig { - fn new(ctx: &LintContext) -> Self { - let mut config = Self::default(); - - for comment in ctx.semantic().comments() { - let content = ctx.source_range(comment.content_span()).trim(); - let Some((directive, rest)) = content.split_once(char::is_whitespace) else { - continue; - }; - let rest = rest.trim(); - - match directive { - "global" | "globals" => parse_global_comment(rest, &mut config.globals), - "exported" => parse_exported_comment(rest, &mut config.exported), - _ => {} - } - } - - config - } -} - -fn parse_global_comment(rest: &str, globals: &mut FxHashMap) { - for item in rest.split(',') { - let item = item.trim(); - if item.is_empty() { - continue; - } - - let (name, value) = item - .split_once(':') - .map_or((item, None), |(name, value)| (name.trim(), Some(value.trim()))); - - if name.is_empty() { - continue; - } - - let value = match value { - Some("writable" | "writeable" | "true") => GlobalValue::Writable, - Some("off") => GlobalValue::Off, - Some("readonly" | "readable" | "false") | None => GlobalValue::Readonly, - Some(_) => continue, - }; - - globals.insert(name.to_string(), value); - } -} - -fn parse_exported_comment(rest: &str, exported: &mut FxHashSet) { - for name in rest.split(|c: char| c == ',' || c.is_whitespace()) { - let name = name.trim(); - if !name.is_empty() { - exported.insert(name.to_string()); - } - } -} - -fn global_variable_value( - name: &str, - ctx: &LintContext, - comment_globals: &FxHashMap, -) -> Option { - if let Some(value) = comment_globals.get(name) { - return Some(*value); - } - +fn global_variable_value(name: &str, ctx: &LintContext) -> Option { if let Some(value) = ctx.globals().get(name) { return Some(*value); } @@ -275,23 +187,20 @@ fn check_declaration( }; if global_value == Some(GlobalValue::Readonly) { - ctx.diagnostic(no_implicit_globals_diagnostic( - DiagnosticKind::RedeclarationOfReadonlyGlobal, - span, - )); + ctx.diagnostic(redeclaration_of_readonly_global_diagnostic(span)); return; } - let diagnostic_kind = match kind { + let diagnostic = match kind { DeclarationKind::Function | DeclarationKind::Var => { - DiagnosticKind::GlobalNonLexicalBinding(kind.as_message_kind()) + global_non_lexical_binding_diagnostic(kind.as_message_kind(), span) } DeclarationKind::Class | DeclarationKind::Let | DeclarationKind::Const => { - DiagnosticKind::GlobalLexicalBinding(kind.as_message_kind()) + global_lexical_binding_diagnostic(kind.as_message_kind(), span) } }; - ctx.diagnostic(no_implicit_globals_diagnostic(diagnostic_kind, span)); + ctx.diagnostic(diagnostic); } #[derive(Debug, Clone, Copy)] @@ -379,14 +288,6 @@ fn test() { ("this.foo = 1;", None), ("this.foo = function() {};", None), ("this.foo = function bar() {};", None), - ("/*global foo:readonly*/", None), - ("/*global foo:writable*/", None), - ("/*global Array:readonly*/", None), - ("/*global Array:writable*/", None), - ("/*global foo:readonly*/", None), // { "globals": { "foo": "readonly" } }, - ("/*global foo:writable*/", None), // { "globals": { "foo": "readonly" } }, - ("/*global foo:readonly*/", None), // { "globals": { "foo": "writable" } }, - ("/*global foo:writable*/", None), // { "globals": { "foo": "writable" } }, ("typeof function() {}", None), ("typeof function foo() {}", None), ("(function() {}) + (function foo() {})", None), @@ -408,10 +309,6 @@ fn test() { Some(serde_json::json!([{ "lexicalBindings": false }])), ), // { "ecmaVersion": 2015 }, ("const Array = 1; let Object; class Math {}", None), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly, bar:readonly, Baz:readonly*/ const foo = 1; let bar; class Baz {}", - None, - ), // { "ecmaVersion": 2015 }, ("typeof class {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, ("typeof class foo {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, ( @@ -447,7 +344,6 @@ fn test() { ("--foo", None), ("foo += 1", None), ("foo ||= 1", None), // { "ecmaVersion": 2021 }, - ("/* global foo: writable*/ foo = bar", None), ("'use strict';foo = 1;", None), ("(function() {'use strict'; foo = 1; })();", None), ("{ class Foo { constructor() { bar = 1; } baz() { bar = 1; } } }", None), // { "ecmaVersion": 2015 }, @@ -464,66 +360,9 @@ fn test() { ("window.foo = function bar() { bar = 1; };", None), // { "globals": globals.browser }, ("window.foo = function bar(baz) { baz = 1; };", None), // { "globals": globals.browser }, ("window.foo = function bar() { var baz; function quux() { quux = 1; } };", None), // { "globals": globals.browser }, - ("/*global foo:writable*/ var foo = 1;", None), - ("/*global foo:writable*/ function *foo() {}", None), // { "ecmaVersion": 2015 }, - ( - "/*global foo:writable*/ const foo = 1;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:writable*/ let foo;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global Foo:writable*/ class Foo {}", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ("/*global foo:writable*/ foo = 1;", None), ("Array.from = 1;", None), ("Object['assign'] = 1;", None), - ("/*global foo:readonly*/ foo.bar = 1;", None), - ("/*global foo:readonly*/ foo++;", None), - ("/*global foo:readonly*/ --foo;", None), - ("/*global foo:readonly*/ foo += 1;", None), - ("/*global foo:readonly*/ foo ||= 1;", None), // { "ecmaVersion": 2021 }, - ("/* exported foo */ var foo = 'foo';", None), - ("/* exported foo */ function foo() {}", None), - ("/* exported foo */ function *foo() {}", None), // { "ecmaVersion": 2015 }, - ("/* exported foo */ async function foo() {}", None), // { "ecmaVersion": 2017 }, - ("/* exported foo */ async function *foo() {}", None), // { "ecmaVersion": 2018 }, - ("/* exported foo */ var foo = function() {};", None), - ("/* exported foo */ var foo = function foo() {};", None), - ("/* exported foo */ var foo = function*() {};", None), // { "ecmaVersion": 2015 }, - ("/* exported foo */ var foo = function *foo() {};", None), // { "ecmaVersion": 2015 }, - ("/* exported foo, bar */ var foo = 1, bar = 2;", None), - ("/* exported a */ const a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ("/* exported a */ let a;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ("/* exported a */ let a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ("/* exported A */ class A {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ( - "/* exported a, b */ const a = 1; const b = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/* exported a, b */ const a = 1, b = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/* exported a, b */ let a, b = 1;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/* exported a, b, C */ const a = 1; let b; class C {}", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/* exported a, b, c */ const [a, b, ...c] = [];", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/* exported a, b, c */ let { a, foo: b, bar: { c } } = {};", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 } + ("foo.bar = 1;", None), ]; let fail = vec![ @@ -560,151 +399,63 @@ fn test() { ("window.foo = { bar() { foo = 1 } }", None), // { "ecmaVersion": 2015 }, ("foo = 1, bar = 2;", None), ("foo = bar = 1", None), - ("/*global foo:writable*/ foo = bar = 1", None), - ("/*global bar:writable*/ foo = bar = 1", None), ("foo = 1; var bar;", None), ("var foo = bar = 1;", None), - ("/*global foo:writable*/ var foo = bar = 1;", None), - ("/*global bar:writable*/ var foo = bar = 1;", None), ("[foo, bar] = [];", None), // { "ecmaVersion": 2015 }, - ("/*global foo:writable*/ [foo, bar] = [];", None), // { "ecmaVersion": 2015 }, - ("/*global bar:writable*/ [foo, bar] = [];", None), // { "ecmaVersion": 2015 }, ("Array = 1", None), ("window = 1;", None), // { "globals": globals.browser }, - ("/*global foo:readonly*/ foo = 1", None), - ("foo = 1;", None), // { "globals": { "foo": "readonly" } }, - ("/*global foo:readonly*/ for (foo in {});", None), - ("/*global foo:readonly*/ for (foo of []);", None), // { "ecmaVersion": 2015 }, ("var Array = 1", None), ("var Array = 1; Array = 2;", None), - ("/*global foo:readonly*/ var foo", None), - ("/*global foo:readonly*/ var foo = 1", None), - ("/*global foo:readonly*/ var foo; foo = 1;", None), - ("/*global foo:readonly*/ for (var foo in obj);", None), - ("/*global foo:readonly*/ for (var foo in obj); foo = 1;", None), - ("/*global foo:readonly*/ for (var foo of arr);", None), // { "ecmaVersion": 2015 }, - ("/*global foo:readonly*/ for (var foo of arr); foo = 1;", None), // { "ecmaVersion": 2015 }, - ("/*global foo:readonly*/ function foo() {}", None), - ( - "/*global foo:readonly*/ const foo = 1", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly*/ const foo = 1; foo = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ("/*global foo:readonly*/ let foo", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly*/ let foo = 1", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly*/ let foo; foo = 1;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global Foo:readonly*/ class Foo {}", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ("/*global foo:readonly, bar: readonly*/ foo = bar = 1", None), - ("/*global foo:writable, bar: readonly*/ foo = bar = 1", None), - ("/*global foo:readonly, bar: writable*/ foo = bar = 1", None), - ("/*global foo: readonly*/ foo = bar = 1", None), - ("/*global bar: readonly*/ foo = bar = 1", None), - ("/*global foo*/ [foo] = arr", None), // { "ecmaVersion": 2015 }, - ("/*global foo, bar: readonly*/ [foo, bar] = arr", None), // { "ecmaVersion": 2015 }, - ("/*global foo: readonly*/ ({ foo } = obj)", None), // { "ecmaVersion": 2015 }, - ("/*global foo: readonly*/ ({ 'a': foo } = obj)", None), // { "ecmaVersion": 2015 }, - ("/*global foo: readonly*/ ({ 'a': { 'b': [foo] } } = obj)", None), // { "ecmaVersion": 2015 }, - ("/*global foo, bar: readonly*/ ({ foo, 'a': bar } = obj)", None), // { "ecmaVersion": 2015 }, - ("/*global foo:readonly, bar: readonly*/ var foo, bar;", None), - ("/*global foo:writable, bar: readonly*/ var foo, bar;", None), - ("/*global foo:readonly, bar: writable*/ var foo, bar;", None), - ("/*global foo:readonly*/ var foo, bar;", None), - ("/*global bar: readonly*/ var foo, bar;", None), - ( - "/*global foo:readonly, bar: readonly*/ const foo = 1, bar = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:writable, bar: readonly*/ const foo = 1, bar = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly, bar: writable*/ const foo = 1, bar = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly*/ const foo = 1, bar = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global bar: readonly*/ const foo = 1, bar = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly, bar: readonly*/ let foo, bar;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:writable, bar: readonly*/ let foo, bar;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly, bar: writable*/ let foo, bar;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global foo:readonly*/ let foo, bar;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/*global bar: readonly*/ let foo, bar;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ("/* exported bar */ var foo = 'text';", None), - ("/* exported bar */ function foo() {}", None), - ("/* exported bar */ function *foo() {}", None), // { "ecmaVersion": 2015 }, - ("/* exported bar */ async function foo() {}", None), // { "ecmaVersion": 2017 }, - ("/* exported bar */ async function *foo() {}", None), // { "ecmaVersion": 2018 }, - ("/* exported bar */ var foo = function() {};", None), - ("/* exported bar */ var foo = function foo() {};", None), - ("/* exported bar */ var foo = function*() {};", None), // { "ecmaVersion": 2015 }, - ("/* exported bar */ var foo = function *foo() {};", None), // { "ecmaVersion": 2015 }, - ("/* exported bar */ var foo = 1, bar = 2;", None), - ("/* exported b */ const a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ("/* exported b */ let a;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ("/* exported b */ let a = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ("/* exported B */ class A {}", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ( - "/* exported a */ const a = 1; const b = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ( - "/* exported a */ const a = 1, b = 2;", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ("/* exported a */ let a, b = 1;", Some(serde_json::json!([{ "lexicalBindings": true }]))), // { "ecmaVersion": 2015 }, - ( - "/* exported a */ const a = 1; let b; class C {}", - Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, + ]; + + Tester::new(NoImplicitGlobals::NAME, NoImplicitGlobals::PLUGIN, pass, fail).test_and_snapshot(); +} + +#[test] +fn test_configured_globals() { + use crate::tester::Tester; + + let writable_globals = Some(serde_json::json!({ + "globals": { + "foo": "writable", + }, + })); + let readonly_globals = Some(serde_json::json!({ + "globals": { + "foo": "readonly", + }, + })); + let disabled_globals = Some(serde_json::json!({ + "globals": { + "foo": "off", + }, + })); + + let pass = vec![ + ("foo = 1;", None, writable_globals.clone()), + ("var foo = 1;", None, writable_globals.clone()), ( - "/* exported a */ const [a, b, ...c] = [];", + "const foo = 1;", Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, + writable_globals, + ), + ("foo.bar = 1;", None, readonly_globals.clone()), + ]; + + let fail = vec![ + ("foo = 1;", None, readonly_globals.clone()), + ("for (foo in {});", None, readonly_globals.clone()), + ("var foo = 1;", None, readonly_globals.clone()), + ("function foo() {}", None, readonly_globals.clone()), ( - "/* exported a */ let { a, foo: b, bar: { c } } = {};", + "const foo = 1;", Some(serde_json::json!([{ "lexicalBindings": true }])), - ), // { "ecmaVersion": 2015 }, - ("/* exported foo */ foo = 1", None), - ("/* exported foo */ foo = function() {};", None), - ("/* exported foo */ foo = function*() {};", None), // { "ecmaVersion": 2015 }, - ("/* exported foo */ window.foo = function() { bar = 1; }", None), - ("/* exported foo */ (function() {}(foo = 1));", None), - ("/* exported foo */ for (foo in {});", None), - ("/* exported foo */ for (foo of []);", None), // { "ecmaVersion": 2015 } + readonly_globals, + ), + ("foo = 1;", None, disabled_globals), ]; - Tester::new(NoImplicitGlobals::NAME, NoImplicitGlobals::PLUGIN, pass, fail).test_and_snapshot(); + Tester::new(NoImplicitGlobals::NAME, NoImplicitGlobals::PLUGIN, pass, fail) + .with_snapshot_suffix("configured-globals") + .test_and_snapshot(); } diff --git a/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap b/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap index 0ba8485235614..cafb09c221c02 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals.snap @@ -296,20 +296,6 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Declare the variable if it is intended to be local. - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /*global foo:writable*/ foo = bar = 1 - · ─────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:25] - 1 │ /*global bar:writable*/ foo = bar = 1 - · ───────────── - ╰──── - help: Declare the variable if it is intended to be local. - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. ╭─[no_implicit_globals.tsx:1:14] 1 │ foo = 1; var bar; @@ -338,20 +324,6 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Declare the variable if it is intended to be local. - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:35] - 1 │ /*global foo:writable*/ var foo = bar = 1; - · ─────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global bar:writable*/ var foo = bar = 1; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - ⚠ eslint(no-implicit-globals): Global variable leak. ╭─[no_implicit_globals.tsx:1:1] 1 │ [foo, bar] = []; @@ -366,20 +338,6 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Declare the variable if it is intended to be local. - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:25] - 1 │ /*global foo:writable*/ [foo, bar] = []; - · ─────────────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:25] - 1 │ /*global bar:writable*/ [foo, bar] = []; - · ─────────────── - ╰──── - help: Declare the variable if it is intended to be local. - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. ╭─[no_implicit_globals.tsx:1:1] 1 │ Array = 1 @@ -393,31 +351,6 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Declare the variable if it is intended to be local. - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:25] - 1 │ /*global foo:readonly*/ foo = 1 - · ─────── - ╰──── - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:1] - 1 │ foo = 1; - · ─────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:25] - 1 │ /*global foo:readonly*/ for (foo in {}); - · ──────────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:25] - 1 │ /*global foo:readonly*/ for (foo of []); - · ──────────────── - ╰──── - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. ╭─[no_implicit_globals.tsx:1:5] 1 │ var Array = 1 @@ -429,545 +362,3 @@ source: crates/oxc_linter/src/tester.rs 1 │ var Array = 1; Array = 2; · ───── ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ var foo - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ var foo = 1 - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ var foo; foo = 1; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /*global foo:readonly*/ for (var foo in obj); - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /*global foo:readonly*/ for (var foo in obj); foo = 1; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /*global foo:readonly*/ for (var foo of arr); - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /*global foo:readonly*/ for (var foo of arr); foo = 1; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /*global foo:readonly*/ function foo() {} - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /*global foo:readonly*/ const foo = 1 - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /*global foo:readonly*/ const foo = 1; foo = 2; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ let foo - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ let foo = 1 - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ let foo; foo = 1; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /*global Foo:readonly*/ class Foo {} - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:46] - 1 │ /*global foo:readonly, bar: readonly*/ foo = bar = 1 - · ─────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:40] - 1 │ /*global foo:readonly, bar: readonly*/ foo = bar = 1 - · ───────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:46] - 1 │ /*global foo:writable, bar: readonly*/ foo = bar = 1 - · ─────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:40] - 1 │ /*global foo:readonly, bar: writable*/ foo = bar = 1 - · ───────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:32] - 1 │ /*global foo: readonly*/ foo = bar = 1 - · ─────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:26] - 1 │ /*global foo: readonly*/ foo = bar = 1 - · ───────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:32] - 1 │ /*global bar: readonly*/ foo = bar = 1 - · ─────── - ╰──── - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:26] - 1 │ /*global bar: readonly*/ foo = bar = 1 - · ───────────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:16] - 1 │ /*global foo*/ [foo] = arr - · ─────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /*global foo, bar: readonly*/ [foo, bar] = arr - · ──────────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /*global foo, bar: readonly*/ [foo, bar] = arr - · ──────────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:27] - 1 │ /*global foo: readonly*/ ({ foo } = obj) - · ───────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:27] - 1 │ /*global foo: readonly*/ ({ 'a': foo } = obj) - · ────────────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:27] - 1 │ /*global foo: readonly*/ ({ 'a': { 'b': [foo] } } = obj) - · ───────────────────────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:32] - 1 │ /*global foo, bar: readonly*/ ({ foo, 'a': bar } = obj) - · ─────────────────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. - ╭─[no_implicit_globals.tsx:1:32] - 1 │ /*global foo, bar: readonly*/ ({ foo, 'a': bar } = obj) - · ─────────────────────── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:49] - 1 │ /*global foo:readonly, bar: readonly*/ var foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:44] - 1 │ /*global foo:readonly, bar: readonly*/ var foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:49] - 1 │ /*global foo:writable, bar: readonly*/ var foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:44] - 1 │ /*global foo:readonly, bar: writable*/ var foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /*global foo:readonly*/ var foo, bar; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ var foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:35] - 1 │ /*global bar: readonly*/ var foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:30] - 1 │ /*global bar: readonly*/ var foo, bar; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:55] - 1 │ /*global foo:readonly, bar: readonly*/ const foo = 1, bar = 2; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:46] - 1 │ /*global foo:readonly, bar: readonly*/ const foo = 1, bar = 2; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:55] - 1 │ /*global foo:writable, bar: readonly*/ const foo = 1, bar = 2; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:46] - 1 │ /*global foo:readonly, bar: writable*/ const foo = 1, bar = 2; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:40] - 1 │ /*global foo:readonly*/ const foo = 1, bar = 2; - · ─── - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /*global foo:readonly*/ const foo = 1, bar = 2; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:41] - 1 │ /*global bar: readonly*/ const foo = 1, bar = 2; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:32] - 1 │ /*global bar: readonly*/ const foo = 1, bar = 2; - · ─── - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:49] - 1 │ /*global foo:readonly, bar: readonly*/ let foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:44] - 1 │ /*global foo:readonly, bar: readonly*/ let foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:49] - 1 │ /*global foo:writable, bar: readonly*/ let foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:44] - 1 │ /*global foo:readonly, bar: writable*/ let foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /*global foo:readonly*/ let foo, bar; - · ─── - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /*global foo:readonly*/ let foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. - ╭─[no_implicit_globals.tsx:1:35] - 1 │ /*global bar: readonly*/ let foo, bar; - · ─── - ╰──── - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:30] - 1 │ /*global bar: readonly*/ let foo, bar; - · ─── - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported bar */ var foo = 'text'; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:29] - 1 │ /* exported bar */ function foo() {} - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:30] - 1 │ /* exported bar */ function *foo() {} - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:35] - 1 │ /* exported bar */ async function foo() {} - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected function declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:36] - 1 │ /* exported bar */ async function *foo() {} - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported bar */ var foo = function() {}; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported bar */ var foo = function foo() {}; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported bar */ var foo = function*() {}; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported bar */ var foo = function *foo() {}; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected 'var' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported bar */ var foo = 1, bar = 2; - · ─── - ╰──── - help: Wrap it in an IIFE for a local variable, or assign it as a global property for a global variable. - - ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported b */ const a = 1; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:22] - 1 │ /* exported b */ let a; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:22] - 1 │ /* exported b */ let a = 1; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected class declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:24] - 1 │ /* exported B */ class A {} - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:37] - 1 │ /* exported a */ const a = 1; const b = 2; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:31] - 1 │ /* exported a */ const a = 1, b = 2; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:25] - 1 │ /* exported a */ let a, b = 1; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected class declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:44] - 1 │ /* exported a */ const a = 1; let b; class C {} - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:35] - 1 │ /* exported a */ const a = 1; let b; class C {} - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:34] - 1 │ /* exported a */ const [a, b, ...c] = []; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'const' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:28] - 1 │ /* exported a */ const [a, b, ...c] = []; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:42] - 1 │ /* exported a */ let { a, foo: b, bar: { c } } = {}; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Unexpected 'let' declaration in the global scope. - ╭─[no_implicit_globals.tsx:1:32] - 1 │ /* exported a */ let { a, foo: b, bar: { c } } = {}; - · ─ - ╰──── - help: Wrap it in a block or in an IIFE. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:20] - 1 │ /* exported foo */ foo = 1 - · ─────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:20] - 1 │ /* exported foo */ foo = function() {}; - · ─────────────────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:20] - 1 │ /* exported foo */ foo = function*() {}; - · ──────────────────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:46] - 1 │ /* exported foo */ window.foo = function() { bar = 1; } - · ─────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:35] - 1 │ /* exported foo */ (function() {}(foo = 1)); - · ─────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:20] - 1 │ /* exported foo */ for (foo in {}); - · ──────────────── - ╰──── - help: Declare the variable if it is intended to be local. - - ⚠ eslint(no-implicit-globals): Global variable leak. - ╭─[no_implicit_globals.tsx:1:20] - 1 │ /* exported foo */ for (foo of []); - · ──────────────── - ╰──── - help: Declare the variable if it is intended to be local. diff --git a/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals@configured-globals.snap b/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals@configured-globals.snap new file mode 100644 index 0000000000000..d8dec0d24b884 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/eslint_no_implicit_globals@configured-globals.snap @@ -0,0 +1,40 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = 1; + · ─────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected assignment to read-only global variable. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ for (foo in {}); + · ──────────────── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:5] + 1 │ var foo = 1; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:10] + 1 │ function foo() {} + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Unexpected redeclaration of read-only global variable. + ╭─[no_implicit_globals.tsx:1:7] + 1 │ const foo = 1; + · ─── + ╰──── + + ⚠ eslint(no-implicit-globals): Global variable leak. + ╭─[no_implicit_globals.tsx:1:1] + 1 │ foo = 1; + · ─────── + ╰──── + help: Declare the variable if it is intended to be local.