oxc/crates/oxc_linter/src/rules/eslint/no_global_assign.rs
2024-06-29 23:23:36 +08:00

110 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{CompactStr, Span};
use crate::{context::LintContext, rule::Rule};
fn no_global_assign_diagnostic(x0: &str, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"eslint(no-global-assign): Read-only global '{x0}' should not be modified."
))
.with_label(span1.label(format!("Read-only global '{x0}' should not be modified.")))
}
#[derive(Debug, Default, Clone)]
pub struct NoGlobalAssign(Box<NoGlobalAssignConfig>);
#[derive(Debug, Default, Clone)]
pub struct NoGlobalAssignConfig {
excludes: Vec<CompactStr>,
}
impl std::ops::Deref for NoGlobalAssign {
type Target = NoGlobalAssignConfig;
fn deref(&self) -> &Self::Target {
&self.0
}
}
declare_oxc_lint!(
/// ### What it does
/// Disallow modifications to read-only global variables.
///
/// ### Why is this bad?
/// In almost all cases, you dont want to assign a value to these global variables as doing so could result in losing access to important functionality.
///
/// ### Example
/// ```javascript
/// Object = null
/// ```
NoGlobalAssign,
correctness
);
impl Rule for NoGlobalAssign {
fn from_configuration(value: serde_json::Value) -> Self {
let obj = value.get(0);
Self(Box::new(NoGlobalAssignConfig {
excludes: obj
.and_then(|v| v.get("exceptions"))
.and_then(serde_json::Value::as_array)
.unwrap_or(&vec![])
.iter()
.map(serde_json::Value::as_str)
.filter(Option::is_some)
.map(|x| x.unwrap().into())
.collect::<Vec<CompactStr>>(),
}))
}
fn run_once(&self, ctx: &LintContext) {
let symbol_table = ctx.symbols();
for reference_id_list in ctx.scopes().root_unresolved_references().values() {
for &reference_id in reference_id_list {
let reference = symbol_table.get_reference(reference_id);
if reference.is_write() {
let name = reference.name();
// Vec::contains isn't working here, but this has the same
// effect and time complexity.
if !self.excludes.iter().any(|e| e == name) && ctx.env_contains_var(name) {
ctx.diagnostic(no_global_assign_diagnostic(name, reference.span()));
}
}
}
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
("string='1';", None),
("var string;", None),
("Object = 0;", Some(serde_json::json!([{ "exceptions": ["Object"] }]))),
("top = 0;", None),
// ("onload = 0;", None), // env: { browser: true }
("require = 0;", None),
("window[parseInt('42', 10)] = 99;", None),
// ("a = 1", None), // globals: { a: true } },
// ("/*global a:true*/ a = 1", None),
];
let fail = vec![
("String = 'hello world';", None),
("String++;", None),
("({Object = 0, String = 0} = {});", None),
// ("top = 0;", None), // env: { browser: true },
// ("require = 0;", None), // env: { node: true },
("function f() { Object = 1; }", None),
// ("/*global b:false*/ function f() { b = 1; }", None),
// ("/*global b:false*/ function f() { b++; }", None),
// ("/*global b*/ b = 1;", None),
("Array = 1;", None),
];
Tester::new(NoGlobalAssign::NAME, pass, fail).test_and_snapshot();
}