feat(linter): add infrastructure for handling early errors

This commit is contained in:
Boshen 2023-03-08 16:29:45 +08:00
parent 8f26b9932a
commit 4e0d785b25
7 changed files with 89 additions and 12 deletions

2
Cargo.lock generated
View file

@ -896,8 +896,10 @@ dependencies = [
"oxc_allocator",
"oxc_ast",
"oxc_diagnostics",
"oxc_linter",
"oxc_parser",
"oxc_printer",
"oxc_semantic",
"pico-args",
"rayon",
"regex",

View file

@ -13,15 +13,18 @@ use std::{fs, rc::Rc};
pub use fixer::{Fixer, Message};
pub(crate) use oxc_semantic::AstNode;
use oxc_semantic::Semantic;
use rule::Rule;
use crate::{
context::LintContext,
rules::{RuleEnum, RULES},
rules::{early_error::javascript::EarlyErrorJavaScript, RuleEnum, RULES},
};
#[derive(Debug)]
pub struct Linter {
rules: Vec<RuleEnum>,
early_error_javascript: EarlyErrorJavaScript,
}
impl Linter {
@ -41,7 +44,12 @@ impl Linter {
.collect()
},
);
Self { rules }
Self::from_rules(rules)
}
#[must_use]
pub fn from_rules(rules: Vec<RuleEnum>) -> Self {
Self { rules, early_error_javascript: EarlyErrorJavaScript }
}
#[must_use]
@ -63,12 +71,7 @@ impl Linter {
},
);
Self { rules }
}
#[must_use]
pub fn from_rules(rules: Vec<RuleEnum>) -> Self {
Self { rules }
Self::from_rules(rules)
}
#[must_use]
@ -81,6 +84,7 @@ impl Linter {
let ctx = LintContext::new(source_text, semantic.clone(), fix);
for node in semantic.nodes().iter() {
self.early_error_javascript.run(node, &ctx);
for rule in &self.rules {
rule.run(node, &ctx);
}
@ -89,6 +93,20 @@ impl Linter {
ctx.into_message()
}
#[must_use]
pub fn run_early_error<'a>(
&self,
semantic: &Rc<Semantic<'a>>,
source_text: &'a str,
fix: bool,
) -> Vec<Message<'a>> {
let ctx = LintContext::new(source_text, semantic.clone(), fix);
for node in semantic.nodes().iter() {
self.early_error_javascript.run(node, &ctx);
}
ctx.into_message()
}
fn read_rules_configuration() -> Option<serde_json::Map<String, serde_json::Value>> {
fs::read_to_string(".eslintrc.json")
.ok()

View file

@ -1,3 +1,7 @@
pub mod early_error {
pub mod javascript;
}
oxc_macros::declare_all_lint_rules! {
constructor_super,
eq_eq_eq,

View file

@ -0,0 +1,33 @@
#[allow(clippy::wildcard_imports)]
use oxc_ast::{ast::*, AstKind, Span};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Default, Clone)]
pub struct EarlyErrorJavaScript;
impl Rule for EarlyErrorJavaScript {
#[allow(clippy::single_match)]
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.get().kind() {
AstKind::RegExpLiteral(lit) => check_regexp_literal(lit, ctx),
_ => {}
}
}
}
fn check_regexp_literal(lit: &RegExpLiteral, ctx: &LintContext) {
#[derive(Debug, Error, Diagnostic)]
#[error("The 'u' and 'v' regular expression flags cannot be enabled at the same time")]
#[diagnostic()]
struct RegExpFlagUAndV(#[label] Span);
let flags = lit.regex.flags;
if flags.contains(RegExpFlags::U | RegExpFlags::V) {
ctx.diagnostic(RegExpFlagUAndV(lit.span));
}
}

View file

@ -16,6 +16,8 @@ oxc_parser = { path = "../../crates/oxc_parser" }
oxc_ast = { path = "../../crates/oxc_ast" }
oxc_printer = { path = "../../crates/oxc_printer" }
oxc_diagnostics = { path = "../../crates/oxc_diagnostics" }
oxc_semantic = { path = "../../crates/oxc_semantic" }
oxc_linter = { path = "../../crates/oxc_linter" }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }

View file

@ -3,6 +3,7 @@ use std::{
io::{stdout, Read, Write},
panic::{catch_unwind, UnwindSafe},
path::{Path, PathBuf},
rc::Rc,
result::Result,
};
@ -12,7 +13,9 @@ use encoding_rs_io::DecodeReaderBytesBuilder;
use oxc_allocator::Allocator;
use oxc_ast::SourceType;
use oxc_diagnostics::miette::{GraphicalReportHandler, GraphicalTheme, NamedSource};
use oxc_linter::Linter;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use rayon::prelude::*;
use similar::{ChangeTag, TextDiff};
use walkdir::WalkDir;
@ -262,12 +265,21 @@ pub trait Case: Sized + Sync + Send + UnwindSafe {
let allocator = Allocator::default();
let source_text = self.code();
let ret = Parser::new(&allocator, source_text, source_type).parse();
let result = if ret.errors.is_empty() {
let program = allocator.alloc(ret.program);
let trivias = Rc::new(ret.trivias);
let semantic = SemanticBuilder::new(source_type).build(program, trivias);
let result = Linter::new().run_early_error(&Rc::new(semantic), source_text, false);
let errors = result
.into_iter()
.map(|msg| msg.error)
.chain(ret.errors.into_iter())
.collect::<Vec<_>>();
let result = if errors.is_empty() {
Ok(String::new())
} else {
let handler = GraphicalReportHandler::new_themed(GraphicalTheme::unicode_nocolor());
let mut output = String::new();
for error in ret.errors {
for error in errors {
let error = error.with_source_code(NamedSource::new(
self.path().to_string_lossy(),
source_text.to_string(),

View file

@ -1,14 +1,13 @@
Test262 Summary:
AST Parsed : 44000/44009 (99.98%)
Positive Passed: 44000/44009 (99.98%)
Negative Passed: 1934/3917 (49.37%)
Negative Passed: 1935/3917 (49.40%)
Expect Syntax Error: "annexB/language/expressions/template-literal/legacy-octal-escape-sequence-strict.js"
Expect Syntax Error: "annexB/language/statements/for-in/const-initializer.js"
Expect Syntax Error: "annexB/language/statements/for-in/let-initializer.js"
Expect Syntax Error: "annexB/language/statements/for-in/strict-initializer.js"
Expect Syntax Error: "annexB/language/statements/for-in/var-arraybindingpattern-initializer.js"
Expect Syntax Error: "annexB/language/statements/for-in/var-objectbindingpattern-initializer.js"
Expect Syntax Error: "built-ins/RegExp/prototype/unicodeSets/uv-flags.js"
Expect Syntax Error: "language/arguments-object/10.5-1gs.js"
Expect Syntax Error: "language/block-scope/syntax/for-in/disallow-initialization-assignment.js"
Expect Syntax Error: "language/block-scope/syntax/for-in/disallow-multiple-lexical-bindings-with-and-without-initializer.js"
@ -2071,6 +2070,13 @@ Expect to Parse: "language/statements/class/decorator/syntax/class-valid/decorat
· ─────
╰────
× The 'u' and 'v' regular expression flags cannot be enabled at the same time
╭─[built-ins/RegExp/prototype/unicodeSets/uv-flags.js:16:1]
16 │
17 │ /./uv;
· ─────
╰────
× Automatic Semicolon Insertion
╭─[language/asi/S7.9.2_A1_T1.js:15:1]
15 │ //CHECK#1