Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/oxc_language_server/src/linter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
18 changes: 15 additions & 3 deletions crates/oxc_linter/src/context/host.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions crates/oxc_linter/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
23 changes: 15 additions & 8 deletions crates/oxc_linter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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")]
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
use oxc_ast::ast::*;
use oxc_semantic::{Semantic, SymbolId};
use oxc_syntax::module_record::ModuleRecord;

use super::{symbol::Symbol, NoUnusedVars};

#[derive(Clone, Copy)]
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)
}
}

Expand Down Expand Up @@ -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> {
Expand Down
9 changes: 7 additions & 2 deletions crates/oxc_linter/src/rules/eslint/no_unused_vars/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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));
Expand Down
12 changes: 9 additions & 3 deletions crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand All @@ -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]
Expand Down Expand Up @@ -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())
}

Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/import/named.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/import/unambiguous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
}
Expand Down
3 changes: 1 addition & 2 deletions crates/oxc_linter/src/rules/oxc/no_barrel_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/react/jsx_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand All @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions crates/oxc_linter/src/service/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/utils/jest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
))
Expand Down
6 changes: 1 addition & 5 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
//! Semantic Builder

use std::{
cell::{Cell, RefCell},
sync::Arc,
};
use std::cell::{Cell, RefCell};

use rustc_hash::FxHashMap;

Expand Down Expand Up @@ -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,
Expand Down
Loading