mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(semantic)!: remove ModuleRecord from Semantic (#7548)
`ModuleRecord` will eventually be moved to be linter specific thing for cross module data sharing, which means we can add more data to it.
This commit is contained in:
parent
8a788b8f4b
commit
0be5233c84
18 changed files with 91 additions and 55 deletions
|
|
@ -290,9 +290,10 @@ impl IsolatedLintHandler {
|
|||
return Some(Self::wrap_diagnostics(path, &source_text, reports, start));
|
||||
};
|
||||
|
||||
let module_record = Arc::new(ret.module_record);
|
||||
let mut semantic = semantic_ret.semantic;
|
||||
semantic.set_irregular_whitespaces(ret.irregular_whitespaces);
|
||||
let result = self.linter.run(path, Rc::new(semantic));
|
||||
let result = self.linter.run(path, Rc::new(semantic), module_record);
|
||||
|
||||
let reports = result
|
||||
.into_iter()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc};
|
||||
|
||||
use oxc_semantic::Semantic;
|
||||
use oxc_span::SourceType;
|
||||
use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use crate::{
|
||||
config::{LintConfig, LintPlugins},
|
||||
|
|
@ -37,6 +39,8 @@ pub(crate) struct ContextHost<'a> {
|
|||
/// Shared semantic information about the file being linted, which includes scopes, symbols
|
||||
/// and AST nodes. See [`Semantic`].
|
||||
pub(super) semantic: Rc<Semantic<'a>>,
|
||||
/// Cross module information.
|
||||
pub(super) module_record: Arc<ModuleRecord>,
|
||||
/// Information about specific rules that should be disabled or enabled, via comment directives like
|
||||
/// `eslint-disable` or `eslint-disable-next-line`.
|
||||
pub(super) disable_directives: DisableDirectives<'a>,
|
||||
|
|
@ -67,6 +71,7 @@ impl<'a> ContextHost<'a> {
|
|||
pub fn new<P: AsRef<Path>>(
|
||||
file_path: P,
|
||||
semantic: Rc<Semantic<'a>>,
|
||||
module_record: Arc<ModuleRecord>,
|
||||
options: LintOptions,
|
||||
config: Arc<LintConfig>,
|
||||
) -> Self {
|
||||
|
|
@ -87,6 +92,7 @@ impl<'a> ContextHost<'a> {
|
|||
|
||||
Self {
|
||||
semantic,
|
||||
module_record,
|
||||
disable_directives,
|
||||
diagnostics: RefCell::new(Vec::with_capacity(DIAGNOSTICS_INITIAL_CAPACITY)),
|
||||
fix: options.fix,
|
||||
|
|
@ -119,6 +125,12 @@ impl<'a> ContextHost<'a> {
|
|||
&self.semantic
|
||||
}
|
||||
|
||||
/// Shared reference to the [`ModuleRecord`] of the file.
|
||||
#[inline]
|
||||
pub fn module_record(&self) -> &ModuleRecord {
|
||||
&self.module_record
|
||||
}
|
||||
|
||||
/// Path to the file being linted.
|
||||
///
|
||||
/// When created from a [`LintService`](`crate::service::LintService`), this
|
||||
|
|
@ -214,9 +226,9 @@ impl<'a> ContextHost<'a> {
|
|||
if self.plugins.has_test() {
|
||||
// let mut test_flags = FrameworkFlags::empty();
|
||||
|
||||
let vitest_like = frameworks::has_vitest_imports(self.semantic.module_record());
|
||||
let vitest_like = frameworks::has_vitest_imports(self.module_record());
|
||||
let jest_like = frameworks::is_jestlike_file(&self.file_path)
|
||||
|| frameworks::has_jest_imports(self.semantic.module_record());
|
||||
|| frameworks::has_jest_imports(self.module_record());
|
||||
|
||||
self.frameworks.set(FrameworkFlags::Vitest, vitest_like);
|
||||
self.frameworks.set(FrameworkFlags::Jest, jest_like);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use oxc_cfg::ControlFlowGraph;
|
|||
use oxc_diagnostics::{OxcDiagnostic, Severity};
|
||||
use oxc_semantic::Semantic;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use crate::rule::RuleFixMeta;
|
||||
|
|
@ -105,6 +106,11 @@ impl<'a> LintContext<'a> {
|
|||
&self.parent.semantic
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn module_record(&self) -> &ModuleRecord {
|
||||
self.parent.module_record()
|
||||
}
|
||||
|
||||
/// Get the control flow graph for the current program.
|
||||
#[inline]
|
||||
pub fn cfg(&self) -> &ControlFlowGraph {
|
||||
|
|
|
|||
|
|
@ -21,14 +21,10 @@ mod utils;
|
|||
pub mod loader;
|
||||
pub mod table;
|
||||
|
||||
use crate::config::ResolvedLinterState;
|
||||
use std::{io::Write, path::Path, rc::Rc, sync::Arc};
|
||||
|
||||
use config::{ConfigStore, LintConfig};
|
||||
use context::ContextHost;
|
||||
use options::LintOptions;
|
||||
use oxc_semantic::{AstNode, Semantic};
|
||||
use utils::iter_possible_jest_call_node;
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
pub use crate::{
|
||||
builder::{LinterBuilder, LinterBuilderError},
|
||||
|
|
@ -41,10 +37,15 @@ pub use crate::{
|
|||
service::{LintService, LintServiceOptions},
|
||||
};
|
||||
use crate::{
|
||||
config::{OxlintEnv, OxlintGlobals, OxlintSettings},
|
||||
config::{
|
||||
ConfigStore, LintConfig, OxlintEnv, OxlintGlobals, OxlintSettings, ResolvedLinterState,
|
||||
},
|
||||
context::ContextHost,
|
||||
fixer::{Fixer, Message},
|
||||
options::LintOptions,
|
||||
rules::RuleEnum,
|
||||
table::RuleTable,
|
||||
utils::iter_possible_jest_call_node,
|
||||
};
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
|
|
@ -110,10 +111,16 @@ impl Linter {
|
|||
self.config.rules()
|
||||
}
|
||||
|
||||
pub fn run<'a>(&self, path: &Path, semantic: Rc<Semantic<'a>>) -> Vec<Message<'a>> {
|
||||
pub fn run<'a>(
|
||||
&self,
|
||||
path: &Path,
|
||||
semantic: Rc<Semantic<'a>>,
|
||||
module_record: Arc<ModuleRecord>,
|
||||
) -> Vec<Message<'a>> {
|
||||
// Get config + rules for this file. Takes base rules and applies glob-based overrides.
|
||||
let ResolvedLinterState { rules, config } = self.config.resolve(path);
|
||||
let ctx_host = Rc::new(ContextHost::new(path, semantic, self.options, config));
|
||||
let ctx_host =
|
||||
Rc::new(ContextHost::new(path, semantic, module_record, self.options, config));
|
||||
|
||||
let rules = rules
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
//! consider variables ignored by name pattern, but by where they are declared.
|
||||
use oxc_ast::{ast::*, AstKind};
|
||||
use oxc_semantic::{NodeId, Semantic};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use super::{options::ArgsOption, NoUnusedVars, Symbol};
|
||||
use crate::rules::eslint::no_unused_vars::binding_pattern::{BindingContext, HasAnyUsedBinding};
|
||||
|
|
@ -120,6 +121,7 @@ impl NoUnusedVars {
|
|||
pub(super) fn is_allowed_argument<'a>(
|
||||
&self,
|
||||
semantic: &Semantic<'a>,
|
||||
module_record: &ModuleRecord,
|
||||
symbol: &Symbol<'_, 'a>,
|
||||
param: &FormalParameter<'a>,
|
||||
) -> bool {
|
||||
|
|
@ -178,7 +180,7 @@ impl NoUnusedVars {
|
|||
return false;
|
||||
}
|
||||
|
||||
let ctx = BindingContext { options: self, semantic };
|
||||
let ctx = BindingContext { options: self, semantic, module_record };
|
||||
params
|
||||
.items
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use oxc_ast::ast::*;
|
||||
use oxc_semantic::{Semantic, SymbolId};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use super::{symbol::Symbol, NoUnusedVars};
|
||||
|
||||
|
|
@ -7,16 +8,18 @@ use super::{symbol::Symbol, NoUnusedVars};
|
|||
pub(super) struct BindingContext<'s, 'a> {
|
||||
pub options: &'s NoUnusedVars,
|
||||
pub semantic: &'s Semantic<'a>,
|
||||
pub module_record: &'s ModuleRecord,
|
||||
}
|
||||
|
||||
impl<'s, 'a> BindingContext<'s, 'a> {
|
||||
#[inline]
|
||||
pub fn symbol(&self, symbol_id: SymbolId) -> Symbol<'s, 'a> {
|
||||
Symbol::new(self.semantic, symbol_id)
|
||||
pub fn symbol(&self, module_record: &'s ModuleRecord, symbol_id: SymbolId) -> Symbol<'s, 'a> {
|
||||
Symbol::new(self.semantic, module_record, symbol_id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_usages(&self, symbol_id: SymbolId) -> bool {
|
||||
self.symbol(symbol_id).has_usages(self.options)
|
||||
pub fn has_usages(&self, symbol_id: SymbolId, module_record: &'s ModuleRecord) -> bool {
|
||||
self.symbol(module_record, symbol_id).has_usages(self.options)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +47,7 @@ impl<'a> HasAnyUsedBinding<'a> for BindingPatternKind<'a> {
|
|||
|
||||
impl<'a> HasAnyUsedBinding<'a> for BindingIdentifier<'a> {
|
||||
fn has_any_used_binding(&self, ctx: BindingContext<'_, 'a>) -> bool {
|
||||
ctx.has_usages(self.symbol_id())
|
||||
ctx.has_usages(self.symbol_id(), ctx.module_record)
|
||||
}
|
||||
}
|
||||
impl<'a> HasAnyUsedBinding<'a> for ObjectPattern<'a> {
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ impl Rule for NoUnusedVars {
|
|||
}
|
||||
|
||||
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
|
||||
let symbol = Symbol::new(ctx.semantic().as_ref(), symbol_id);
|
||||
let symbol = Symbol::new(ctx.semantic().as_ref(), ctx.module_record(), symbol_id);
|
||||
if Self::should_skip_symbol(&symbol) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -294,7 +294,12 @@ impl NoUnusedVars {
|
|||
});
|
||||
}
|
||||
AstKind::FormalParameter(param) => {
|
||||
if self.is_allowed_argument(ctx.semantic().as_ref(), symbol, param) {
|
||||
if self.is_allowed_argument(
|
||||
ctx.semantic().as_ref(),
|
||||
ctx.module_record(),
|
||||
symbol,
|
||||
param,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
ctx.diagnostic(diagnostic::param(symbol, &self.args_ignore_pattern));
|
||||
|
|
|
|||
|
|
@ -12,10 +12,12 @@ use oxc_semantic::{
|
|||
SymbolTable,
|
||||
};
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct Symbol<'s, 'a> {
|
||||
semantic: &'s Semantic<'a>,
|
||||
module_record: &'s ModuleRecord,
|
||||
id: SymbolId,
|
||||
flags: SymbolFlags,
|
||||
span: OnceCell<Span>,
|
||||
|
|
@ -29,9 +31,13 @@ impl PartialEq for Symbol<'_, '_> {
|
|||
|
||||
// constructor and simple getters
|
||||
impl<'s, 'a> Symbol<'s, 'a> {
|
||||
pub fn new(semantic: &'s Semantic<'a>, symbol_id: SymbolId) -> Self {
|
||||
pub fn new(
|
||||
semantic: &'s Semantic<'a>,
|
||||
module_record: &'s ModuleRecord,
|
||||
symbol_id: SymbolId,
|
||||
) -> Self {
|
||||
let flags = semantic.symbols().get_flags(symbol_id);
|
||||
Self { semantic, id: symbol_id, flags, span: OnceCell::new() }
|
||||
Self { semantic, module_record, id: symbol_id, flags, span: OnceCell::new() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -172,7 +178,7 @@ impl<'a> Symbol<'_, 'a> {
|
|||
pub fn is_exported(&self) -> bool {
|
||||
let is_in_exportable_scope = self.is_root() || self.is_in_ts_namespace();
|
||||
is_in_exportable_scope
|
||||
&& (self.semantic.module_record().exported_bindings.contains_key(self.name())
|
||||
&& (self.module_record.exported_bindings.contains_key(self.name())
|
||||
|| self.in_export_node())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ impl Rule for Named {
|
|||
return;
|
||||
}
|
||||
|
||||
let module_record = semantic.module_record();
|
||||
let module_record = ctx.module_record();
|
||||
|
||||
for import_entry in &module_record.import_entries {
|
||||
// Get named import
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ declare_oxc_lint!(
|
|||
/// <https://github.com/import-js/eslint-plugin-import/blob/v2.29.1/docs/rules/unambiguous.md>
|
||||
impl Rule for Unambiguous {
|
||||
fn run_once(&self, ctx: &LintContext<'_>) {
|
||||
if ctx.semantic().module_record().not_esm {
|
||||
if ctx.module_record().not_esm {
|
||||
ctx.diagnostic(unambiguous_diagnostic(Span::default()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,8 +69,7 @@ impl Rule for NoBarrelFile {
|
|||
}
|
||||
|
||||
fn run_once(&self, ctx: &LintContext<'_>) {
|
||||
let semantic = ctx.semantic();
|
||||
let module_record = semantic.module_record();
|
||||
let module_record = ctx.module_record();
|
||||
|
||||
if module_record.not_esm {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ pub fn import_matcher<'a>(
|
|||
expected_module_name: &'a str,
|
||||
) -> bool {
|
||||
let expected_module_name = expected_module_name.cow_to_lowercase();
|
||||
ctx.semantic().module_record().import_entries.iter().any(|import| {
|
||||
ctx.module_record().import_entries.iter().any(|import| {
|
||||
import.module_request.name().as_str() == expected_module_name
|
||||
&& import.local_name.name().as_str() == actual_local_name
|
||||
})
|
||||
|
|
@ -109,7 +109,7 @@ pub fn is_import<'a>(
|
|||
expected_local_name: &'a str,
|
||||
expected_module_name: &'a str,
|
||||
) -> bool {
|
||||
if ctx.semantic().module_record().requested_modules.is_empty()
|
||||
if ctx.module_record().requested_modules.is_empty()
|
||||
&& ctx.scopes().get_bindings(ctx.scopes().root_scope_id()).is_empty()
|
||||
{
|
||||
return actual_local_name == expected_local_name;
|
||||
|
|
|
|||
|
|
@ -297,9 +297,8 @@ impl Runtime {
|
|||
};
|
||||
|
||||
let mut semantic = semantic_ret.semantic;
|
||||
semantic.set_module_record(&module_record);
|
||||
semantic.set_irregular_whitespaces(ret.irregular_whitespaces);
|
||||
self.linter.run(path, Rc::new(semantic))
|
||||
self.linter.run(path, Rc::new(semantic), Arc::clone(&module_record))
|
||||
}
|
||||
|
||||
pub(super) fn init_cache_state(&self, path: &Path) -> bool {
|
||||
|
|
|
|||
|
|
@ -321,10 +321,12 @@ mod test {
|
|||
SemanticBuilder::new().with_cfg(true).build(&parser_ret.program).semantic;
|
||||
let semantic_ret = Rc::new(semantic_ret);
|
||||
|
||||
let module_record = Arc::new(parser_ret.module_record);
|
||||
let build_ctx = |path: &'static str| {
|
||||
Rc::new(ContextHost::new(
|
||||
path,
|
||||
Rc::clone(&semantic_ret),
|
||||
Arc::clone(&module_record),
|
||||
LintOptions::default(),
|
||||
Arc::default(),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
//! Semantic Builder
|
||||
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
|
|
@ -289,7 +286,6 @@ impl<'a> SemanticBuilder<'a> {
|
|||
source_type: self.source_type,
|
||||
comments,
|
||||
irregular_whitespaces: [].into(),
|
||||
module_record: Arc::new(oxc_syntax::module_record::ModuleRecord::default()),
|
||||
nodes: self.nodes,
|
||||
scopes: self.scope,
|
||||
symbols: self.symbols,
|
||||
|
|
|
|||
|
|
@ -6,14 +6,12 @@
|
|||
//! ```
|
||||
|
||||
use std::ops::RangeBounds;
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxc_ast::{
|
||||
ast::IdentifierReference, comments_range, has_comments_between, AstKind, Comment, CommentsRange,
|
||||
};
|
||||
use oxc_cfg::ControlFlowGraph;
|
||||
use oxc_span::{GetSpan, SourceType, Span};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
pub use oxc_syntax::{
|
||||
scope::{ScopeFlags, ScopeId},
|
||||
symbol::{SymbolFlags, SymbolId},
|
||||
|
|
@ -81,8 +79,6 @@ pub struct Semantic<'a> {
|
|||
comments: &'a oxc_allocator::Vec<'a, Comment>,
|
||||
irregular_whitespaces: Box<[Span]>,
|
||||
|
||||
module_record: Arc<ModuleRecord>,
|
||||
|
||||
/// Parsed JSDoc comments.
|
||||
jsdoc: JSDocFinder<'a>,
|
||||
|
||||
|
|
@ -134,10 +130,6 @@ impl<'a> Semantic<'a> {
|
|||
self.irregular_whitespaces = irregular_whitespaces;
|
||||
}
|
||||
|
||||
pub fn set_module_record(&mut self, module_record: &Arc<ModuleRecord>) {
|
||||
self.module_record = Arc::clone(module_record);
|
||||
}
|
||||
|
||||
/// Trivias (comments) found while parsing
|
||||
pub fn comments(&self) -> &[Comment] {
|
||||
self.comments
|
||||
|
|
@ -165,11 +157,6 @@ impl<'a> Semantic<'a> {
|
|||
&self.jsdoc
|
||||
}
|
||||
|
||||
/// ESM module record containing imports and exports.
|
||||
pub fn module_record(&self) -> &ModuleRecord {
|
||||
self.module_record.as_ref()
|
||||
}
|
||||
|
||||
/// [`SymbolTable`] containing all symbols in the program and their
|
||||
/// [`Reference`]s.
|
||||
pub fn symbols(&self) -> &SymbolTable {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use std::{
|
|||
cell::{Cell, RefCell},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use oxc::{
|
||||
|
|
@ -20,6 +21,7 @@ use oxc::{
|
|||
ScopeFlags, ScopeId, ScopeTree, SemanticBuilder, SymbolTable,
|
||||
},
|
||||
span::SourceType,
|
||||
syntax::module_record::ModuleRecord,
|
||||
transformer::{TransformOptions, Transformer},
|
||||
};
|
||||
use oxc_index::Idx;
|
||||
|
|
@ -198,7 +200,7 @@ impl Oxc {
|
|||
.preserve_parens
|
||||
.unwrap_or(default_parser_options.preserve_parens),
|
||||
};
|
||||
let ParserReturn { mut program, errors, .. } =
|
||||
let ParserReturn { mut program, errors, module_record, .. } =
|
||||
Parser::new(&allocator, source_text, source_type)
|
||||
.with_options(oxc_parser_options)
|
||||
.parse();
|
||||
|
|
@ -227,7 +229,8 @@ impl Oxc {
|
|||
);
|
||||
}
|
||||
|
||||
self.run_linter(&run_options, &path, &program);
|
||||
let module_record = Arc::new(module_record);
|
||||
self.run_linter(&run_options, &path, &program, &module_record);
|
||||
|
||||
self.run_prettier(&run_options, source_text, source_type);
|
||||
|
||||
|
|
@ -296,12 +299,19 @@ impl Oxc {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn run_linter(&mut self, run_options: &OxcRunOptions, path: &Path, program: &Program) {
|
||||
fn run_linter(
|
||||
&mut self,
|
||||
run_options: &OxcRunOptions,
|
||||
path: &Path,
|
||||
program: &Program,
|
||||
module_record: &Arc<ModuleRecord>,
|
||||
) {
|
||||
// Only lint if there are no syntax errors
|
||||
if run_options.lint.unwrap_or_default() && self.diagnostics.borrow().is_empty() {
|
||||
let semantic_ret = SemanticBuilder::new().with_cfg(true).build(program);
|
||||
let semantic = Rc::new(semantic_ret.semantic);
|
||||
let linter_ret = Linter::default().run(path, Rc::clone(&semantic));
|
||||
let linter_ret =
|
||||
Linter::default().run(path, Rc::clone(&semantic), Arc::clone(module_record));
|
||||
let diagnostics = linter_ret.into_iter().map(|e| e.error).collect();
|
||||
self.save_diagnostics(diagnostics);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{env, path::Path, rc::Rc};
|
||||
use std::{env, path::Path, rc::Rc, sync::Arc};
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
|
|
@ -39,7 +39,8 @@ fn bench_linter(criterion: &mut Criterion) {
|
|||
.build(&ret.program);
|
||||
let linter = LinterBuilder::all().with_fix(FixKind::All).build();
|
||||
let semantic = Rc::new(semantic_ret.semantic);
|
||||
b.iter(|| linter.run(path, Rc::clone(&semantic)));
|
||||
let module_record = Arc::new(ret.module_record);
|
||||
b.iter(|| linter.run(path, Rc::clone(&semantic), Arc::clone(&module_record)));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue