feat(linter): implement no-global-assign (#624)

closes #598
This commit is contained in:
SoonIter 2023-07-28 10:20:38 +08:00 committed by GitHub
parent 6631336f5c
commit 786cf8252f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 0 deletions

View file

@ -36,6 +36,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::no_eval,
eslint::no_ex_assign,
eslint::no_func_assign,
eslint::no_global_assign,
eslint::no_import_assign,
eslint::no_inner_declarations,
eslint::no_mixed_operators,

View file

@ -0,0 +1,111 @@
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::{self, Error},
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{Atom, Span};
use crate::{context::LintContext, globals::BUILTINS, rule::Rule};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint(no-global-assign): Read-only global '{0}' should not be modified.")]
#[diagnostic(severity(warning))]
struct NoGlobalAssignDiagnostic(
Atom,
#[label("Read-only global '{0}' should not be modified.")] pub Span,
);
#[derive(Debug, Default, Clone)]
pub struct NoGlobalAssign {
excludes: Vec<Atom>,
}
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 {
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(std::option::Option::is_some)
.map(|x| Atom::from(x.unwrap().to_string()))
.collect::<Vec<Atom>>(),
}
}
fn run_once(&self, ctx: &LintContext) {
let symbol_table = ctx.semantic().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 symbol_table.is_global_reference(reference_id) && reference.is_write() {
let name = reference.name().clone();
let mut is_global_assign = false;
if let Some(&value) = BUILTINS.get(&name) {
if !value {
is_global_assign = true;
}
}
if self.excludes.contains(&name.clone()) {
is_global_assign = false;
}
if is_global_assign {
ctx.diagnostic(NoGlobalAssignDiagnostic(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),
// ("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();
}

View file

@ -0,0 +1,47 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_global_assign
---
⚠ eslint(no-global-assign): Read-only global 'String' should not be modified.
╭─[no_global_assign.tsx:1:1]
1 │ String = 'hello world';
· ───┬──
· ╰── Read-only global 'String' should not be modified.
╰────
⚠ eslint(no-global-assign): Read-only global 'String' should not be modified.
╭─[no_global_assign.tsx:1:1]
1 │ String++;
· ───┬──
· ╰── Read-only global 'String' should not be modified.
╰────
⚠ eslint(no-global-assign): Read-only global 'String' should not be modified.
╭─[no_global_assign.tsx:1:1]
1 │ ({Object = 0, String = 0} = {});
· ───┬──
· ╰── Read-only global 'String' should not be modified.
╰────
⚠ eslint(no-global-assign): Read-only global 'Object' should not be modified.
╭─[no_global_assign.tsx:1:1]
1 │ ({Object = 0, String = 0} = {});
· ───┬──
· ╰── Read-only global 'Object' should not be modified.
╰────
⚠ eslint(no-global-assign): Read-only global 'Object' should not be modified.
╭─[no_global_assign.tsx:1:1]
1 │ function f() { Object = 1; }
· ───┬──
· ╰── Read-only global 'Object' should not be modified.
╰────
⚠ eslint(no-global-assign): Read-only global 'Array' should not be modified.
╭─[no_global_assign.tsx:1:1]
1 │ Array = 1;
· ──┬──
· ╰── Read-only global 'Array' should not be modified.
╰────