Skip to content

Commit 5d5278d

Browse files
committed
fix(linter): no-global-assign look for globals entry too
1 parent 41ad42a commit 5d5278d

File tree

4 files changed

+124
-21
lines changed

4 files changed

+124
-21
lines changed

crates/oxc_linter/src/context/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ impl<'a> LintContext<'a> {
157157
&& !self.globals().get(name).is_some_and(|value| *value == GlobalValue::Off)
158158
}
159159

160+
/// Checks if the provided identifier is a reference to a global variable.
161+
pub fn get_global_variable_value(&self, name: &str) -> Option<GlobalValue> {
162+
if !self.scopes().root_unresolved_references().contains_key(name) {
163+
return None;
164+
}
165+
166+
if let Some(value) = self.globals().get(name) {
167+
return Some(*value);
168+
}
169+
170+
self.get_env_global_entry(name)
171+
}
172+
160173
/// Runtime environments turned on/off by the user.
161174
///
162175
/// Examples of environments are `builtin`, `browser`, `node`, etc.
@@ -165,6 +178,23 @@ impl<'a> LintContext<'a> {
165178
&self.parent.config.env
166179
}
167180

181+
fn get_env_global_entry(&self, var: &str) -> Option<GlobalValue> {
182+
// builtin is always readonly
183+
if GLOBALS["builtin"].contains_key(var) {
184+
return Some(GlobalValue::Readonly);
185+
}
186+
187+
for env in self.env().iter() {
188+
if let Some(env) = GLOBALS.get(env) {
189+
if let Some(value) = env.get(var) {
190+
return Some(GlobalValue::from(*value));
191+
};
192+
}
193+
}
194+
195+
None
196+
}
197+
168198
/// Checks if a given variable named is defined as a global variable in the current environment.
169199
///
170200
/// Example:

crates/oxc_linter/src/javascript_globals.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
2626
"Int32Array" => false,
2727
"Int8Array" => false,
2828
"Intl" => false,
29+
"Iterator" => false,
2930
"JSON" => false,
3031
"Map" => false,
3132
"Math" => false,
@@ -432,6 +433,7 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
432433
"CSSKeywordValue" => false,
433434
"CSSLayerBlockRule" => false,
434435
"CSSLayerStatementRule" => false,
436+
"CSSMarginRule" => false,
435437
"CSSMathClamp" => false,
436438
"CSSMathInvert" => false,
437439
"CSSMathMax" => false,
@@ -443,6 +445,7 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
443445
"CSSMatrixComponent" => false,
444446
"CSSMediaRule" => false,
445447
"CSSNamespaceRule" => false,
448+
"CSSNestedDeclarations" => false,
446449
"CSSNumericArray" => false,
447450
"CSSNumericValue" => false,
448451
"CSSPageDescriptors" => false,
@@ -754,7 +757,6 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
754757
"InputEvent" => false,
755758
"IntersectionObserver" => false,
756759
"IntersectionObserverEntry" => false,
757-
"Iterator" => false,
758760
"Keyboard" => false,
759761
"KeyboardEvent" => false,
760762
"KeyboardLayoutMap" => false,
@@ -1216,12 +1218,15 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
12161218
"XRDOMOverlayState" => false,
12171219
"XRDepthInformation" => false,
12181220
"XRFrame" => false,
1221+
"XRHand" => false,
12191222
"XRHitTestResult" => false,
12201223
"XRHitTestSource" => false,
12211224
"XRInputSource" => false,
12221225
"XRInputSourceArray" => false,
12231226
"XRInputSourceEvent" => false,
12241227
"XRInputSourcesChangeEvent" => false,
1228+
"XRJointPose" => false,
1229+
"XRJointSpace" => false,
12251230
"XRLayer" => false,
12261231
"XRLightEstimate" => false,
12271232
"XRLightProbe" => false,
@@ -1489,6 +1494,7 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
14891494
"BroadcastChannel" => false,
14901495
"Buffer" => false,
14911496
"ByteLengthQueuingStrategy" => false,
1497+
"CloseEvent" => false,
14921498
"CompressionStream" => false,
14931499
"CountQueuingStrategy" => false,
14941500
"Crypto" => false,
@@ -1501,7 +1507,6 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
15011507
"File" => false,
15021508
"FormData" => false,
15031509
"Headers" => false,
1504-
"Iterator" => false,
15051510
"MessageChannel" => false,
15061511
"MessageEvent" => false,
15071512
"MessagePort" => false,
@@ -1564,6 +1569,7 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
15641569
"Blob" => false,
15651570
"BroadcastChannel" => false,
15661571
"ByteLengthQueuingStrategy" => false,
1572+
"CloseEvent" => false,
15671573
"CompressionStream" => false,
15681574
"CountQueuingStrategy" => false,
15691575
"Crypto" => false,
@@ -1576,7 +1582,6 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
15761582
"File" => false,
15771583
"FormData" => false,
15781584
"Headers" => false,
1579-
"Iterator" => false,
15801585
"MessageChannel" => false,
15811586
"MessageEvent" => false,
15821587
"MessagePort" => false,
@@ -1718,6 +1723,10 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
17181723
"GPUTextureView" => false,
17191724
"GPUUncapturedErrorEvent" => false,
17201725
"GPUValidationError" => false,
1726+
"HID" => false,
1727+
"HIDConnectionEvent" => false,
1728+
"HIDDevice" => false,
1729+
"HIDInputReportEvent" => false,
17211730
"Headers" => false,
17221731
"IDBCursor" => false,
17231732
"IDBCursorWithValue" => false,
@@ -1737,7 +1746,6 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
17371746
"ImageDecoder" => false,
17381747
"ImageTrack" => false,
17391748
"ImageTrackList" => false,
1740-
"Iterator" => false,
17411749
"Lock" => false,
17421750
"LockManager" => false,
17431751
"MediaCapabilities" => false,
@@ -1772,6 +1780,7 @@ pub static GLOBALS: Map<&'static str, Map<&'static str, bool>> = phf_map! {
17721780
"PushManager" => false,
17731781
"PushSubscription" => false,
17741782
"PushSubscriptionOptions" => false,
1783+
"RTCDataChannel" => false,
17751784
"RTCEncodedAudioFrame" => false,
17761785
"RTCEncodedVideoFrame" => false,
17771786
"ReadableByteStreamController" => false,

crates/oxc_linter/src/rules/eslint/no_global_assign.rs

+60-17
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use oxc_diagnostics::OxcDiagnostic;
22
use oxc_macros::declare_oxc_lint;
33
use oxc_span::{CompactStr, Span};
44

5-
use crate::{context::LintContext, rule::Rule};
5+
use crate::{config::GlobalValue, context::LintContext, rule::Rule};
66

77
fn no_global_assign_diagnostic(global_name: &str, span: Span) -> OxcDiagnostic {
88
OxcDiagnostic::warn(format!("Read-only global '{global_name}' should not be modified."))
@@ -65,7 +65,9 @@ impl Rule for NoGlobalAssign {
6565
let reference = symbol_table.get_reference(reference_id);
6666
if reference.is_write()
6767
&& !self.excludes.iter().any(|n| n == name)
68-
&& ctx.env_contains_var(name)
68+
&& ctx
69+
.get_global_variable_value(name)
70+
.is_some_and(|global| global == GlobalValue::Readonly)
6971
{
7072
ctx.diagnostic(no_global_assign_diagnostic(
7173
name,
@@ -82,28 +84,69 @@ fn test() {
8284
use crate::tester::Tester;
8385

8486
let pass = vec![
85-
("string='1';", None),
86-
("var string;", None),
87-
("Object = 0;", Some(serde_json::json!([{ "exceptions": ["Object"] }]))),
88-
("top = 0;", None),
89-
// ("onload = 0;", None), // env: { browser: true }
90-
("require = 0;", None),
91-
("window[parseInt('42', 10)] = 99;", None),
92-
// ("a = 1", None), // globals: { a: true } },
87+
("string='1';", None, None),
88+
("var string;", None, None),
89+
("Object = 0;", Some(serde_json::json!([{ "exceptions": ["Object"] }])), None),
90+
("top = 0;", None, None),
91+
(
92+
"onload = 0;",
93+
None,
94+
Some(serde_json::json!({
95+
"env": {
96+
"browser": true
97+
}
98+
})),
99+
),
100+
("require = 0;", None, None),
101+
("window[parseInt('42', 10)] = 99;", None, None),
102+
(
103+
"a = 1",
104+
None,
105+
Some(serde_json::json!({
106+
"globals": {
107+
"a": true
108+
}
109+
})),
110+
),
93111
// ("/*global a:true*/ a = 1", None),
94112
];
95113

96114
let fail = vec![
97-
("String = 'hello world';", None),
98-
("String++;", None),
99-
("({Object = 0, String = 0} = {});", None),
100-
// ("top = 0;", None), // env: { browser: true },
101-
// ("require = 0;", None), // env: { node: true },
102-
("function f() { Object = 1; }", None),
115+
("String = 'hello world';", None, None),
116+
("String++;", None, None),
117+
("({Object = 0, String = 0} = {});", None, None),
118+
(
119+
"top = 0;",
120+
None,
121+
Some(serde_json::json!({
122+
"env": {
123+
"browser": true
124+
}
125+
})),
126+
),
127+
(
128+
"require = 0;",
129+
None,
130+
Some(serde_json::json!({
131+
"env": {
132+
"node": true
133+
}
134+
})),
135+
),
136+
("function f() { Object = 1; }", None, None),
137+
(
138+
"a = 1",
139+
None,
140+
Some(serde_json::json!({
141+
"globals": {
142+
"a": false
143+
}
144+
})),
145+
),
103146
// ("/*global b:false*/ function f() { b = 1; }", None),
104147
// ("/*global b:false*/ function f() { b++; }", None),
105148
// ("/*global b*/ b = 1;", None),
106-
("Array = 1;", None),
149+
("Array = 1;", None, None),
107150
];
108151

109152
Tester::new(NoGlobalAssign::NAME, NoGlobalAssign::PLUGIN, pass, fail).test_and_snapshot();

crates/oxc_linter/src/snapshots/eslint_no_global_assign.snap

+21
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,34 @@ source: crates/oxc_linter/src/tester.rs
2929
· ╰── Read-only global 'Object' should not be modified.
3030
╰────
3131

32+
eslint(no-global-assign): Read-only global 'top' should not be modified.
33+
╭─[no_global_assign.tsx:1:1]
34+
1top = 0;
35+
· ─┬─
36+
· ╰── Read-only global 'top' should not be modified.
37+
╰────
38+
39+
eslint(no-global-assign): Read-only global 'require' should not be modified.
40+
╭─[no_global_assign.tsx:1:1]
41+
1require = 0;
42+
· ───┬───
43+
· ╰── Read-only global 'require' should not be modified.
44+
╰────
45+
3246
eslint(no-global-assign): Read-only global 'Object' should not be modified.
3347
╭─[no_global_assign.tsx:1:16]
3448
1function f() { Object = 1; }
3549
· ───┬──
3650
· ╰── Read-only global 'Object' should not be modified.
3751
╰────
3852

53+
eslint(no-global-assign): Read-only global 'a' should not be modified.
54+
╭─[no_global_assign.tsx:1:1]
55+
1a = 1
56+
· ┬
57+
· ╰── Read-only global 'a' should not be modified.
58+
╰────
59+
3960
eslint(no-global-assign): Read-only global 'Array' should not be modified.
4061
╭─[no_global_assign.tsx:1:1]
4162
1Array = 1;

0 commit comments

Comments
 (0)