diff --git a/Cargo.lock b/Cargo.lock index 35810be9bca9d..668bece50ec6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1986,7 +1986,6 @@ version = "0.38.0" dependencies = [ "assert-unchecked", "bitflags 2.6.0", - "dashmap 6.1.0", "nonmax", "oxc_allocator", "oxc_ast_macros", diff --git a/crates/oxc_language_server/src/linter.rs b/crates/oxc_language_server/src/linter.rs index b92f25e98f929..6722198902f20 100644 --- a/crates/oxc_language_server/src/linter.rs +++ b/crates/oxc_language_server/src/linter.rs @@ -13,7 +13,7 @@ use oxc_allocator::Allocator; use oxc_diagnostics::{Error, NamedSource, Severity}; use oxc_linter::{ loader::{JavaScriptSource, Loader}, - FixKind, Linter, + FixKind, Linter, ModuleRecord, }; use oxc_parser::{ParseOptions, Parser}; use oxc_semantic::SemanticBuilder; @@ -290,7 +290,7 @@ impl IsolatedLintHandler { return Some(Self::wrap_diagnostics(path, &source_text, reports, start)); }; - let module_record = Arc::new(ret.module_record); + let module_record = Arc::new(ModuleRecord::new(path, &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), module_record); diff --git a/crates/oxc_linter/src/context/host.rs b/crates/oxc_linter/src/context/host.rs index 15cdf30ace115..d44590447af2d 100644 --- a/crates/oxc_linter/src/context/host.rs +++ b/crates/oxc_linter/src/context/host.rs @@ -2,13 +2,13 @@ use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc}; use oxc_semantic::Semantic; use oxc_span::SourceType; -use oxc_syntax::module_record::ModuleRecord; use crate::{ config::{LintConfig, LintPlugins}, disable_directives::{DisableDirectives, DisableDirectivesBuilder}, fixer::{FixKind, Message}, frameworks, + module_record::ModuleRecord, options::LintOptions, utils, FrameworkFlags, RuleWithSeverity, }; diff --git a/crates/oxc_linter/src/context/mod.rs b/crates/oxc_linter/src/context/mod.rs index d02c0d568221c..ea63e0c87daf7 100644 --- a/crates/oxc_linter/src/context/mod.rs +++ b/crates/oxc_linter/src/context/mod.rs @@ -7,7 +7,6 @@ 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; @@ -15,7 +14,7 @@ use crate::{ disable_directives::DisableDirectives, fixer::{FixKind, Message, RuleFix, RuleFixer}, javascript_globals::GLOBALS, - AllowWarnDeny, FrameworkFlags, OxlintEnv, OxlintGlobals, OxlintSettings, + AllowWarnDeny, FrameworkFlags, ModuleRecord, OxlintEnv, OxlintGlobals, OxlintSettings, }; pub(crate) use host::ContextHost; diff --git a/crates/oxc_linter/src/frameworks.rs b/crates/oxc_linter/src/frameworks.rs index 7d4de0a5f95ad..67705ed40d0ef 100644 --- a/crates/oxc_linter/src/frameworks.rs +++ b/crates/oxc_linter/src/frameworks.rs @@ -1,7 +1,8 @@ use std::{hash, path::Path}; use bitflags::bitflags; -use oxc_syntax::module_record::ModuleRecord; + +use crate::ModuleRecord; bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index 06fa743a0bfdb..03a89a6e3086c 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -12,6 +12,8 @@ mod fixer; mod frameworks; mod globals; mod javascript_globals; +mod module_graph_visitor; +mod module_record; mod options; mod rule; mod rules; @@ -24,7 +26,6 @@ pub mod table; use std::{io::Write, path::Path, rc::Rc, sync::Arc}; use oxc_semantic::{AstNode, Semantic}; -use oxc_syntax::module_record::ModuleRecord; pub use crate::{ builder::{LinterBuilder, LinterBuilderError}, @@ -32,6 +33,7 @@ pub use crate::{ context::LintContext, fixer::FixKind, frameworks::FrameworkFlags, + module_record::ModuleRecord, options::{AllowWarnDeny, InvalidFilterKind, LintFilter, LintFilterKind}, rule::{RuleCategory, RuleFixMeta, RuleMeta, RuleWithSeverity}, service::{LintService, LintServiceOptions}, diff --git a/crates/oxc_syntax/src/module_graph_visitor.rs b/crates/oxc_linter/src/module_graph_visitor.rs similarity index 91% rename from crates/oxc_syntax/src/module_graph_visitor.rs rename to crates/oxc_linter/src/module_graph_visitor.rs index 31082b3805c54..3d6bb7214e77a 100644 --- a/crates/oxc_syntax/src/module_graph_visitor.rs +++ b/crates/oxc_linter/src/module_graph_visitor.rs @@ -4,7 +4,7 @@ use std::{marker::PhantomData, path::PathBuf, sync::Arc}; use oxc_span::CompactStr; use rustc_hash::FxHashSet; -use crate::module_record::ModuleRecord; +use crate::ModuleRecord; type ModulePair<'a> = (&'a CompactStr, &'a Arc); @@ -73,19 +73,19 @@ impl<'a, T> ModuleGraphVisitorBuilder<'a, T> { self } - /// Sets the enter module event closure. - #[must_use] - pub fn enter(mut self, enter: F) -> Self { - self.enter = Some(Box::new(enter)); - self - } + // /// Sets the enter module event closure. + // #[must_use] + // pub fn enter(mut self, enter: F) -> Self { + // self.enter = Some(Box::new(enter)); + // self + // } - /// Sets the leave module event closure. - #[must_use] - pub fn leave(mut self, leave: F) -> Self { - self.leave = Some(Box::new(leave)); - self - } + // /// Sets the leave module event closure. + // #[must_use] + // pub fn leave(mut self, leave: F) -> Self { + // self.leave = Some(Box::new(leave)); + // self + // } /// Behaves similar to a flat fold_while iteration. pub fn visit_fold VisitFoldWhile>( @@ -125,13 +125,13 @@ impl Default for ModuleGraphVisitorBuilder<'_, T> { pub struct ModuleGraphVisitResult { pub result: T, - pub traversed: FxHashSet, - pub max_depth: u32, + pub _traversed: FxHashSet, + pub _max_depth: u32, } impl ModuleGraphVisitResult { fn with_result(result: T, visitor: ModuleGraphVisitor) -> Self { - Self { result, traversed: visitor.traversed, max_depth: visitor.max_depth } + Self { result, _traversed: visitor.traversed, _max_depth: visitor.max_depth } } } diff --git a/crates/oxc_linter/src/module_record.rs b/crates/oxc_linter/src/module_record.rs new file mode 100644 index 0000000000000..c547b0343fe1c --- /dev/null +++ b/crates/oxc_linter/src/module_record.rs @@ -0,0 +1,494 @@ +//! [ECMAScript Module Record](https://tc39.es/ecma262/#sec-abstract-module-records) +#![allow(missing_docs)] // fixme + +use std::{ + fmt, + path::{Path, PathBuf}, + sync::Arc, +}; + +use dashmap::DashMap; +use rustc_hash::{FxBuildHasher, FxHashMap}; + +use oxc_span::{CompactStr, Span}; +pub use oxc_syntax::module_record::RequestedModule; + +type FxDashMap = DashMap; + +/// ESM Module Record +/// +/// All data inside this data structure are for ESM, no commonjs data is allowed. +/// +/// See +/// * +/// * +#[derive(Default)] +pub struct ModuleRecord { + /// This module has no import / export statements + pub not_esm: bool, + + /// Resolved absolute path to this module record + pub resolved_absolute_path: PathBuf, + + /// `[[RequestedModules]]` + /// + /// A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. The List is in source text occurrence order. + /// + /// Module requests from: + /// import ImportClause FromClause + /// import ModuleSpecifier + /// export ExportFromClause FromClause + /// Keyed by ModuleSpecifier, valued by all node occurrences + pub requested_modules: FxHashMap>, + + /// `[[LoadedModules]]` + /// + /// A map from the specifier strings used by the module represented by this record to request + /// the importation of a module to the resolved Module Record. The list does not contain two + /// different Records with the same `[[Specifier]]`. + /// + /// Note that Oxc does not support cross-file analysis, so this map will be empty after + /// [`ModuleRecord`] is created. You must link the module records yourself. + pub loaded_modules: FxDashMap>, + + /// `[[ImportEntries]]` + /// + /// A List of `ImportEntry` records derived from the code of this module + pub import_entries: Vec, + + /// `[[LocalExportEntries]]` + /// + /// A List of `ExportEntry` records derived from the code of this module + /// that correspond to declarations that occur within the module + pub local_export_entries: Vec, + + /// `[[IndirectExportEntries]]` + /// + /// A List of `ExportEntry` records derived from the code of this module + /// that correspond to reexported imports that occur within the module + /// or exports from `export * as namespace` declarations. + pub indirect_export_entries: Vec, + + /// `[[StarExportEntries]]` + /// + /// A List of `ExportEntry` records derived from the code of this module + /// that correspond to `export *` declarations that occur within the module, + /// not including `export * as namespace` declarations. + pub star_export_entries: Vec, + + /// Local exported bindings + pub exported_bindings: FxHashMap, + + /// Local duplicated exported bindings, for diagnostics + pub exported_bindings_duplicated: Vec, + + /// Reexported bindings from `export * from 'specifier'` + /// Keyed by resolved path + pub exported_bindings_from_star_export: FxDashMap>, + + /// `export default name` + /// ^^^^^^^ span + pub export_default: Option, + + /// Duplicated span of `export default` for diagnostics + pub export_default_duplicated: Vec, +} + +impl fmt::Debug for ModuleRecord { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + // recursively formatting loaded modules can crash when the module graph is cyclic + let loaded_modules = self + .loaded_modules + .iter() + .map(|entry| (entry.key().to_string())) + .reduce(|acc, key| format!("{acc}, {key}")) + .unwrap_or_default(); + let loaded_modules = format!("{{ {loaded_modules} }}"); + f.debug_struct("ModuleRecord") + .field("not_esm", &self.not_esm) + .field("resolved_absolute_path", &self.resolved_absolute_path) + .field("requested_modules", &self.requested_modules) + .field("loaded_modules", &loaded_modules) + .field("import_entries", &self.import_entries) + .field("local_export_entries", &self.local_export_entries) + .field("indirect_export_entries", &self.indirect_export_entries) + .field("star_export_entries", &self.star_export_entries) + .field("exported_bindings", &self.exported_bindings) + .field("exported_bindings_duplicated", &self.exported_bindings_duplicated) + .field("exported_bindings_from_star_export", &self.exported_bindings_from_star_export) + .field("export_default", &self.export_default) + .field("export_default_duplicated", &self.export_default_duplicated) + .finish() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NameSpan { + name: CompactStr, + span: Span, +} + +impl NameSpan { + pub fn new(name: CompactStr, span: Span) -> Self { + Self { name, span } + } + + pub const fn name(&self) -> &CompactStr { + &self.name + } + + pub const fn span(&self) -> Span { + self.span + } +} + +impl<'a> From<&oxc_syntax::module_record::NameSpan<'a>> for NameSpan { + fn from(other: &oxc_syntax::module_record::NameSpan<'a>) -> Self { + Self { name: CompactStr::from(other.name.as_str()), span: other.span } + } +} + +/// [`ImportEntry`](https://tc39.es/ecma262/#importentry-record) +/// +/// ## Examples +/// +/// ```ts +/// // _ local_name +/// import v from "mod"; +/// // ^^^ module_request +/// +/// // ____ is_type will be `true` +/// import type { foo as bar } from "mod"; +/// // import_name^^^ ^^^ local_name +/// +/// import * as ns from "mod"; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImportEntry { + /// String value of the ModuleSpecifier of the ImportDeclaration. + /// + /// ## Examples + /// + /// ```ts + /// import { foo } from "mod"; + /// // ^^^ + /// ``` + pub module_request: NameSpan, + + /// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`. + /// + /// ## Examples + /// + /// ```ts + /// import { foo } from "mod"; + /// // ^^^ + /// import { foo as bar } from "mod"; + /// // ^^^ + /// ``` + pub import_name: ImportImportName, + + /// The name that is used to locally access the imported value from within the importing module. + /// + /// ## Examples + /// + /// ```ts + /// import { foo } from "mod"; + /// // ^^^ + /// import { foo as bar } from "mod"; + /// // ^^^ + /// ``` + pub local_name: NameSpan, + + /// Whether this binding is for a TypeScript type-only import. This is a non-standard field. + /// When creating a [`ModuleRecord`] for a JavaScript file, this will always be false. + /// + /// ## Examples + /// + /// `is_type` will be `true` for the following imports: + /// ```ts + /// import type { foo } from "mod"; + /// import { type foo } from "mod"; + /// ``` + /// + /// and will be `false` for these imports: + /// ```ts + /// import { foo } from "mod"; + /// import { foo as type } from "mod"; + /// ``` + pub is_type: bool, +} + +impl<'a> From<&oxc_syntax::module_record::ImportEntry<'a>> for ImportEntry { + fn from(other: &oxc_syntax::module_record::ImportEntry<'a>) -> Self { + Self { + module_request: NameSpan::from(&other.module_request), + import_name: ImportImportName::from(&other.import_name), + local_name: NameSpan::from(&other.local_name), + is_type: other.is_type, + } + } +} + +/// `ImportName` For `ImportEntry` +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ImportImportName { + Name(NameSpan), + NamespaceObject, + Default(Span), +} + +impl ImportImportName { + pub fn is_default(&self) -> bool { + matches!(self, Self::Default(_)) + } + + pub fn is_namespace_object(&self) -> bool { + matches!(self, Self::NamespaceObject) + } +} + +impl<'a> From<&oxc_syntax::module_record::ImportImportName<'a>> for ImportImportName { + fn from(other: &oxc_syntax::module_record::ImportImportName<'a>) -> Self { + match other { + oxc_syntax::module_record::ImportImportName::Name(name_span) => { + Self::Name(NameSpan::from(name_span)) + } + oxc_syntax::module_record::ImportImportName::NamespaceObject => Self::NamespaceObject, + oxc_syntax::module_record::ImportImportName::Default(span) => Self::Default(*span), + } + } +} + +/// [`ExportEntry`](https://tc39.es/ecma262/#exportentry-record) +/// +/// Describes a single exported binding from a module. Named export statements that contain more +/// than one binding produce multiple ExportEntry records. +/// +/// ## Examples +/// +/// ```ts +/// // foo's ExportEntry nas no `module_request` or `import_name. +/// // ___ local_name +/// export { foo }; +/// // ^^^ export_name. Since there's no alias, it's the same as local_name. +/// +/// // re-exports do not produce local bindings, so `local_name` is null. +/// // ___ import_name __ module_request +/// export { foo as bar } from "mod"; +/// // ^^^ export_name +/// +/// ``` +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct ExportEntry { + /// Span for the entire export entry + pub span: Span, + + /// The String value of the ModuleSpecifier of the ExportDeclaration. + /// null if the ExportDeclaration does not have a ModuleSpecifier. + pub module_request: Option, + + /// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`. + /// null if the ExportDeclaration does not have a ModuleSpecifier. + /// "all" is used for `export * as ns from "mod"`` declarations. + /// "all-but-default" is used for `export * from "mod" declarations`. + pub import_name: ExportImportName, + + /// The name used to export this binding by this module. + pub export_name: ExportExportName, + + /// The name that is used to locally access the exported value from within the importing module. + /// null if the exported value is not locally accessible from within the module. + pub local_name: ExportLocalName, +} + +impl<'a> From<&oxc_syntax::module_record::ExportEntry<'a>> for ExportEntry { + fn from(other: &oxc_syntax::module_record::ExportEntry<'a>) -> Self { + Self { + span: other.span, + module_request: other.module_request.as_ref().map(NameSpan::from), + import_name: ExportImportName::from(&other.import_name), + export_name: ExportExportName::from(&other.export_name), + local_name: ExportLocalName::from(&other.local_name), + } + } +} + +/// `ImportName` for `ExportEntry` +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub enum ExportImportName { + Name(NameSpan), + /// all is used for export * as ns from "mod" declarations. + All, + /// all-but-default is used for export * from "mod" declarations. + AllButDefault, + /// the ExportDeclaration does not have a ModuleSpecifier + #[default] + Null, +} + +impl<'a> From<&oxc_syntax::module_record::ExportImportName<'a>> for ExportImportName { + fn from(other: &oxc_syntax::module_record::ExportImportName<'a>) -> Self { + match other { + oxc_syntax::module_record::ExportImportName::Name(name_span) => { + Self::Name(NameSpan::from(name_span)) + } + oxc_syntax::module_record::ExportImportName::All => Self::All, + oxc_syntax::module_record::ExportImportName::AllButDefault => Self::AllButDefault, + oxc_syntax::module_record::ExportImportName::Null => Self::Null, + } + } +} + +impl ExportImportName { + pub fn is_all(&self) -> bool { + matches!(self, Self::All) + } + + pub fn is_all_but_default(&self) -> bool { + matches!(self, Self::AllButDefault) + } +} + +/// `ExportName` for `ExportEntry` +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub enum ExportExportName { + Name(NameSpan), + Default(Span), + #[default] + Null, +} + +impl ExportExportName { + /// Returns `true` if this is [`ExportExportName::Default`]. + pub fn is_default(&self) -> bool { + matches!(self, Self::Default(_)) + } + + /// Returns `true` if this is [`ExportExportName::Null`]. + pub fn is_null(&self) -> bool { + matches!(self, Self::Null) + } + + /// Attempt to get the [`Span`] of this export name. + pub fn span(&self) -> Option { + match self { + Self::Name(name) => Some(name.span()), + Self::Default(span) => Some(*span), + Self::Null => None, + } + } +} + +impl<'a> From<&oxc_syntax::module_record::ExportExportName<'a>> for ExportExportName { + fn from(other: &oxc_syntax::module_record::ExportExportName<'a>) -> Self { + match other { + oxc_syntax::module_record::ExportExportName::Name(name_span) => { + Self::Name(NameSpan::from(name_span)) + } + oxc_syntax::module_record::ExportExportName::Default(span) => Self::Default(*span), + oxc_syntax::module_record::ExportExportName::Null => Self::Null, + } + } +} + +/// `LocalName` for `ExportEntry` +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub enum ExportLocalName { + Name(NameSpan), + /// `export default name_span` + Default(NameSpan), + #[default] + Null, +} + +impl ExportLocalName { + /// `true` if this is a [`ExportLocalName::Default`]. + pub fn is_default(&self) -> bool { + matches!(self, Self::Default(_)) + } + + /// `true` if this is a [`ExportLocalName::Null`]. + pub fn is_null(&self) -> bool { + matches!(self, Self::Null) + } + + /// Get the bound name of this export. [`None`] for [`ExportLocalName::Null`]. + pub const fn name(&self) -> Option<&CompactStr> { + match self { + Self::Name(name) | Self::Default(name) => Some(name.name()), + Self::Null => None, + } + } +} + +impl<'a> From<&oxc_syntax::module_record::ExportLocalName<'a>> for ExportLocalName { + fn from(other: &oxc_syntax::module_record::ExportLocalName<'a>) -> Self { + match other { + oxc_syntax::module_record::ExportLocalName::Name(name_span) => { + Self::Name(NameSpan::from(name_span)) + } + oxc_syntax::module_record::ExportLocalName::Default(name_span) => { + Self::Default(NameSpan::from(name_span)) + } + oxc_syntax::module_record::ExportLocalName::Null => Self::Null, + } + } +} + +impl ModuleRecord { + pub fn new(path: &Path, other: &oxc_syntax::module_record::ModuleRecord) -> Self { + Self { + not_esm: other.not_esm, + resolved_absolute_path: path.to_path_buf(), + requested_modules: other + .requested_modules + .iter() + .map(|(name, requested_modules)| { + ( + CompactStr::from(name.as_str()), + requested_modules.iter().copied().collect::>(), + ) + }) + .collect(), + import_entries: other.import_entries.iter().map(ImportEntry::from).collect(), + + local_export_entries: other + .local_export_entries + .iter() + .map(ExportEntry::from) + .collect(), + indirect_export_entries: other + .indirect_export_entries + .iter() + .map(ExportEntry::from) + .collect(), + star_export_entries: other.star_export_entries.iter().map(ExportEntry::from).collect(), + exported_bindings: other + .exported_bindings + .iter() + .map(|(name, span)| (CompactStr::from(name.as_str()), *span)) + .collect(), + exported_bindings_duplicated: other + .exported_bindings_duplicated + .iter() + .map(NameSpan::from) + .collect(), + exported_bindings_from_star_export: other + .exported_bindings_from_star_export + .iter() + .map(|(name, values)| { + ( + PathBuf::from(name.as_str()), + values + .into_iter() + .map(|v| CompactStr::from(v.as_str())) + .collect::>(), + ) + }) + .collect(), + export_default: other.export_default, + export_default_duplicated: other.export_default_duplicated.iter().copied().collect(), + ..ModuleRecord::default() + } + } +} diff --git a/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs b/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs index 83523dc32a1a8..7cf82fc544204 100644 --- a/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs @@ -3,9 +3,12 @@ use rustc_hash::FxHashMap; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; -use oxc_syntax::module_record::{ExportImportName, ImportImportName}; -use crate::{context::LintContext, rule::Rule}; +use crate::{ + context::LintContext, + module_record::{ExportImportName, ImportImportName}, + rule::Rule, +}; fn no_duplicate_imports_diagnostic(module_name: &str, span: Span, span2: Span) -> OxcDiagnostic { OxcDiagnostic::warn(format!("'{module_name}' import is duplicated")) diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs index 094f05c94642f..a97987a16d8e9 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs @@ -2,10 +2,12 @@ //! 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}; +use crate::{ + rules::eslint::no_unused_vars::binding_pattern::{BindingContext, HasAnyUsedBinding}, + ModuleRecord, +}; impl Symbol<'_, '_> { /// Check if the declaration of this [`Symbol`] is use. diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/binding_pattern.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/binding_pattern.rs index 51243df3eb79c..af8848cd8402d 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/binding_pattern.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/binding_pattern.rs @@ -1,9 +1,10 @@ use oxc_ast::ast::*; use oxc_semantic::{Semantic, SymbolId}; -use oxc_syntax::module_record::ModuleRecord; use super::{symbol::Symbol, NoUnusedVars}; +use crate::ModuleRecord; + #[derive(Clone, Copy)] pub(super) struct BindingContext<'s, 'a> { pub options: &'s NoUnusedVars, diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs index 055c7c6afbc53..231fa1baaad9f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs @@ -12,7 +12,8 @@ use oxc_semantic::{ SymbolTable, }; use oxc_span::{GetSpan, Span}; -use oxc_syntax::module_record::ModuleRecord; + +use crate::ModuleRecord; #[derive(Clone)] pub(super) struct Symbol<'s, 'a> { diff --git a/crates/oxc_linter/src/rules/import/default.rs b/crates/oxc_linter/src/rules/import/default.rs index 96ad67deccadf..61e81b06698d9 100644 --- a/crates/oxc_linter/src/rules/import/default.rs +++ b/crates/oxc_linter/src/rules/import/default.rs @@ -1,9 +1,8 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{Span, VALID_EXTENSIONS}; -use oxc_syntax::module_record::ImportImportName; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, module_record::ImportImportName, rule::Rule}; fn default_diagnostic(imported_name: &str, span: Span) -> OxcDiagnostic { OxcDiagnostic::warn(format!("No default export found in imported module {imported_name:?}")) diff --git a/crates/oxc_linter/src/rules/import/export.rs b/crates/oxc_linter/src/rules/import/export.rs index f9c7847da1558..c05adb9d46ca6 100644 --- a/crates/oxc_linter/src/rules/import/export.rs +++ b/crates/oxc_linter/src/rules/import/export.rs @@ -5,9 +5,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; -use oxc_syntax::module_record::ModuleRecord; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, rule::Rule, ModuleRecord}; fn no_named_export(module_name: &str, span: Span) -> OxcDiagnostic { OxcDiagnostic::warn(format!("No named exports found in module '{module_name}'")) diff --git a/crates/oxc_linter/src/rules/import/named.rs b/crates/oxc_linter/src/rules/import/named.rs index f262f8db38851..a44ce45505651 100644 --- a/crates/oxc_linter/src/rules/import/named.rs +++ b/crates/oxc_linter/src/rules/import/named.rs @@ -1,9 +1,12 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use oxc_syntax::module_record::{ExportImportName, ImportImportName}; -use crate::{context::LintContext, rule::Rule}; +use crate::{ + context::LintContext, + module_record::{ExportImportName, ImportImportName}, + rule::Rule, +}; fn named_diagnostic(imported_name: &str, module_name: &str, span: Span) -> OxcDiagnostic { OxcDiagnostic::warn(format!("named import {imported_name:?} not found")) diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index 7b69b34d3d6f0..2a01ce918fb2f 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -8,11 +8,12 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::AstNode; use oxc_span::{GetSpan, Span}; -use oxc_syntax::module_record::{ - ExportExportName, ExportImportName, ImportImportName, ModuleRecord, -}; -use crate::{context::LintContext, rule::Rule}; +use crate::{ + context::LintContext, + module_record::{ExportExportName, ExportImportName, ImportImportName, ModuleRecord}, + rule::Rule, +}; fn no_export(span: Span, specifier_name: &str, namespace_name: &str) -> OxcDiagnostic { OxcDiagnostic::warn(format!( diff --git a/crates/oxc_linter/src/rules/import/no_cycle.rs b/crates/oxc_linter/src/rules/import/no_cycle.rs index e6096966fecbd..7f4c0d65ed0a4 100644 --- a/crates/oxc_linter/src/rules/import/no_cycle.rs +++ b/crates/oxc_linter/src/rules/import/no_cycle.rs @@ -5,13 +5,14 @@ use cow_utils::CowUtils; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; -use oxc_syntax::{ + +use crate::{ + context::LintContext, module_graph_visitor::{ModuleGraphVisitorBuilder, ModuleGraphVisitorEvent, VisitFoldWhile}, - module_record::ModuleRecord, + rule::Rule, + ModuleRecord, }; -use crate::{context::LintContext, rule::Rule}; - fn no_cycle_diagnostic(span: Span, paths: &str) -> OxcDiagnostic { OxcDiagnostic::warn("Dependency cycle detected") .with_help(format!("These paths form a cycle: \n{paths}")) diff --git a/crates/oxc_linter/src/rules/import/no_duplicates.rs b/crates/oxc_linter/src/rules/import/no_duplicates.rs index 983c4c9261a05..b33caac862117 100644 --- a/crates/oxc_linter/src/rules/import/no_duplicates.rs +++ b/crates/oxc_linter/src/rules/import/no_duplicates.rs @@ -4,10 +4,13 @@ use itertools::Itertools; use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use oxc_syntax::module_record::{ImportImportName, RequestedModule}; use rustc_hash::FxHashMap; -use crate::{context::LintContext, rule::Rule}; +use crate::{ + context::LintContext, + module_record::{ImportImportName, RequestedModule}, + rule::Rule, +}; fn no_duplicates_diagnostic( module_name: &str, diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default.rs b/crates/oxc_linter/src/rules/import/no_named_as_default.rs index dac368d610075..4206fa1ef0ec3 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default.rs @@ -1,9 +1,8 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use oxc_syntax::module_record::ImportImportName; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, module_record::ImportImportName, rule::Rule}; fn no_named_as_default_diagnostic( span: Span, diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs index 4fdc11787a75a..1178d8580347c 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs @@ -6,10 +6,9 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::SymbolId; use oxc_span::Span; -use oxc_syntax::module_record::ImportImportName; use rustc_hash::FxHashMap; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, module_record::ImportImportName, rule::Rule}; fn no_named_as_default_member_dignostic( span: Span, diff --git a/crates/oxc_linter/src/rules/import/no_namespace.rs b/crates/oxc_linter/src/rules/import/no_namespace.rs index da0d9aeb4fcb5..25321cc5f0fc4 100644 --- a/crates/oxc_linter/src/rules/import/no_namespace.rs +++ b/crates/oxc_linter/src/rules/import/no_namespace.rs @@ -3,9 +3,8 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; -use oxc_syntax::module_record::ImportImportName; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, module_record::ImportImportName, rule::Rule}; fn no_namespace_diagnostic(span: Span) -> OxcDiagnostic { OxcDiagnostic::warn("Usage of namespaced aka wildcard \"*\" imports prohibited") diff --git a/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs index 695114055d74c..de643ba0fadfc 100644 --- a/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs +++ b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs @@ -1,12 +1,13 @@ use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; -use oxc_syntax::{ + +use crate::{ + context::LintContext, module_graph_visitor::{ModuleGraphVisitorBuilder, VisitFoldWhile}, - module_record::ModuleRecord, + rule::Rule, + ModuleRecord, }; -use crate::{context::LintContext, rule::Rule}; - fn no_barrel_file(total: usize, threshold: usize, labels: Vec) -> OxcDiagnostic { OxcDiagnostic::warn(format!( "Barrel file detected, {total} modules are loaded." diff --git a/crates/oxc_linter/src/service/module_cache.rs b/crates/oxc_linter/src/service/module_cache.rs index 3bd6d74f8f711..9ae9d2535d123 100644 --- a/crates/oxc_linter/src/service/module_cache.rs +++ b/crates/oxc_linter/src/service/module_cache.rs @@ -7,7 +7,7 @@ use std::{ use dashmap::{mapref::one::Ref, DashMap}; use rustc_hash::{FxBuildHasher, FxHashMap}; -use oxc_syntax::module_record::ModuleRecord; +use crate::ModuleRecord; type FxDashMap = DashMap; diff --git a/crates/oxc_linter/src/service/runtime.rs b/crates/oxc_linter/src/service/runtime.rs index 440c15a397d7f..d7cabe62b04d6 100644 --- a/crates/oxc_linter/src/service/runtime.rs +++ b/crates/oxc_linter/src/service/runtime.rs @@ -7,18 +7,19 @@ use std::{ sync::Arc, }; +use rayon::{iter::ParallelBridge, prelude::ParallelIterator}; +use rustc_hash::FxHashSet; + use oxc_allocator::Allocator; use oxc_diagnostics::{DiagnosticSender, DiagnosticService, Error, OxcDiagnostic}; use oxc_parser::{ParseOptions, Parser}; use oxc_resolver::Resolver; use oxc_semantic::SemanticBuilder; use oxc_span::{SourceType, VALID_EXTENSIONS}; -use oxc_syntax::module_record::ModuleRecord; -use rayon::{iter::ParallelBridge, prelude::ParallelIterator}; -use rustc_hash::FxHashSet; use crate::{ loader::{JavaScriptSource, PartialLoader, LINT_PARTIAL_LOADER_EXT}, + module_record::ModuleRecord, utils::read_to_string, Fixer, Linter, Message, }; @@ -218,7 +219,7 @@ impl Runtime { .with_build_jsdoc(true) .with_check_syntax_error(check_syntax_errors); - let mut module_record = ret.module_record; + let mut module_record = ModuleRecord::new(path, &ret.module_record); module_record.resolved_absolute_path = path.to_path_buf(); let module_record = Arc::new(module_record); diff --git a/crates/oxc_linter/src/snapshots/import_no_duplicates.snap b/crates/oxc_linter/src/snapshots/import_no_duplicates.snap index 3e18d77c1046d..274b55e71d177 100644 --- a/crates/oxc_linter/src/snapshots/import_no_duplicates.snap +++ b/crates/oxc_linter/src/snapshots/import_no_duplicates.snap @@ -18,11 +18,11 @@ snapshot_kind: text ╰──── help: Merge these imports into a single import statement - ⚠ eslint-plugin-import(no-duplicates): Module './bar?optionX' is imported more than once in this file - ╭─[index.ts:1:49] + ⚠ eslint-plugin-import(no-duplicates): Module './bar.js?optionX' is imported more than once in this file + ╭─[index.ts:1:15] 1 │ import x from './bar.js?optionX'; import y from './bar?optionX'; - · ────────────────── ───────┬─────── - · │ ╰── It is first imported here + · ─────────┬──────── ─────────────── + · ╰── It is first imported here ╰──── help: Merge these imports into a single import statement @@ -34,11 +34,11 @@ snapshot_kind: text ╰──── help: Merge these imports into a single import statement - ⚠ eslint-plugin-import(no-duplicates): Module './bar.js?optionX' is imported more than once in this file - ╭─[index.ts:1:46] + ⚠ eslint-plugin-import(no-duplicates): Module './bar?optionX' is imported more than once in this file + ╭─[index.ts:1:15] 1 │ import x from './bar?optionX'; import y from './bar.js?optionX'; - · ─────────────── ─────────┬──────── - · │ ╰── It is first imported here + · ───────┬─────── ────────────────── + · ╰── It is first imported here ╰──── help: Merge these imports into a single import statement diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index 2586ee588d5dd..aa06b4f299120 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -310,7 +310,7 @@ mod test { use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; - use crate::{options::LintOptions, ContextHost}; + use crate::{options::LintOptions, ContextHost, ModuleRecord}; #[test] fn test_is_jest_file() { @@ -321,12 +321,11 @@ 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), + Arc::new(ModuleRecord::default()), LintOptions::default(), Arc::default(), )) diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index 9f999f15a2f23..d6f3ea9f73304 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -154,7 +154,7 @@ pub struct ParserReturn<'a> { pub program: Program<'a>, /// See - pub module_record: ModuleRecord, + pub module_record: ModuleRecord<'a>, /// Syntax errors encountered while parsing. /// @@ -370,7 +370,7 @@ struct ParserImpl<'a> { ast: AstBuilder<'a>, /// Module Record Builder - module_record_builder: ModuleRecordBuilder, + module_record_builder: ModuleRecordBuilder<'a>, /// Precomputed typescript detection is_ts: bool, @@ -389,8 +389,6 @@ impl<'a> ParserImpl<'a> { options: ParseOptions, unique: UniquePromise, ) -> Self { - let mut module_record_builder = ModuleRecordBuilder::default(); - module_record_builder.module_record.not_esm = true; Self { options, lexer: Lexer::new(allocator, source_text, source_type, unique), @@ -402,7 +400,7 @@ impl<'a> ParserImpl<'a> { state: ParserState::default(), ctx: Self::default_context(source_type, options), ast: AstBuilder::new(allocator), - module_record_builder, + module_record_builder: ModuleRecordBuilder::new(allocator), is_ts: source_type.is_typescript(), } } diff --git a/crates/oxc_parser/src/module_record.rs b/crates/oxc_parser/src/module_record.rs index 11817200ec210..d3d323ae1ac3b 100644 --- a/crates/oxc_parser/src/module_record.rs +++ b/crates/oxc_parser/src/module_record.rs @@ -1,19 +1,24 @@ +use oxc_allocator::Allocator; use oxc_ast::ast::*; use oxc_diagnostics::OxcDiagnostic; use oxc_ecmascript::BoundNames; -use oxc_span::{CompactStr, GetSpan, Span}; +use oxc_span::{GetSpan, Span}; use oxc_syntax::module_record::*; use crate::diagnostics; -#[derive(Default)] -pub struct ModuleRecordBuilder { - pub module_record: ModuleRecord, - export_entries: Vec, +pub struct ModuleRecordBuilder<'a> { + allocator: &'a Allocator, + module_record: ModuleRecord<'a>, + export_entries: Vec>, } -impl ModuleRecordBuilder { - pub fn build(mut self) -> ModuleRecord { +impl<'a> ModuleRecordBuilder<'a> { + pub fn new(allocator: &'a Allocator) -> Self { + Self { allocator, module_record: ModuleRecord::new(allocator), export_entries: vec![] } + } + + pub fn build(mut self) -> ModuleRecord<'a> { // The `ParseModule` algorithm requires `importedBoundNames` (import entries) to be // resolved before resolving export entries. self.resolve_export_entries(); @@ -27,12 +32,8 @@ impl ModuleRecordBuilder { // It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. for name_span in &module_record.exported_bindings_duplicated { - let old_span = module_record.exported_bindings[name_span.name()]; - errors.push(diagnostics::duplicate_export( - name_span.name(), - name_span.span(), - old_span, - )); + let old_span = module_record.exported_bindings[&name_span.name]; + errors.push(diagnostics::duplicate_export(&name_span.name, name_span.span, old_span)); } for span in &module_record.export_default_duplicated { @@ -51,35 +52,35 @@ impl ModuleRecordBuilder { errors } - fn add_module_request(&mut self, name_span: &NameSpan, is_type: bool, is_import: bool) { + fn add_module_request(&mut self, name_span: &NameSpan<'a>, is_type: bool, is_import: bool) { self.module_record .requested_modules - .entry(name_span.name().clone()) - .or_default() - .push(RequestedModule::new(name_span.span(), is_type, is_import)); + .entry(name_span.name.clone()) + .or_insert_with(|| oxc_allocator::Vec::new_in(self.allocator)) + .push(RequestedModule::new(name_span.span, is_type, is_import)); } - fn add_import_entry(&mut self, entry: ImportEntry) { + fn add_import_entry(&mut self, entry: ImportEntry<'a>) { self.module_record.import_entries.push(entry); } - fn add_export_entry(&mut self, entry: ExportEntry) { + fn add_export_entry(&mut self, entry: ExportEntry<'a>) { self.export_entries.push(entry); } - fn append_local_export_entry(&mut self, entry: ExportEntry) { + fn append_local_export_entry(&mut self, entry: ExportEntry<'a>) { self.module_record.local_export_entries.push(entry); } - fn append_indirect_export_entry(&mut self, entry: ExportEntry) { + fn append_indirect_export_entry(&mut self, entry: ExportEntry<'a>) { self.module_record.indirect_export_entries.push(entry); } - fn append_star_export_entry(&mut self, entry: ExportEntry) { + fn append_star_export_entry(&mut self, entry: ExportEntry<'a>) { self.module_record.star_export_entries.push(entry); } - fn add_export_binding(&mut self, name: CompactStr, span: Span) { + fn add_export_binding(&mut self, name: Atom<'a>, span: Span) { if let Some(old_node) = self.module_record.exported_bindings.insert(name.clone(), span) { self.module_record.exported_bindings_duplicated.push(NameSpan::new(name, old_node)); } @@ -104,7 +105,7 @@ impl ModuleRecordBuilder { .module_record .import_entries .iter() - .find(|entry| entry.local_name.name() == name.name()), + .find(|entry| entry.local_name.name == name.name), _ => None, }; match found_import_entry { @@ -164,7 +165,7 @@ impl ModuleRecordBuilder { } } - pub fn visit_module_declaration(&mut self, module_decl: &ModuleDeclaration) { + pub fn visit_module_declaration(&mut self, module_decl: &ModuleDeclaration<'a>) { self.module_record.not_esm = false; match module_decl { ModuleDeclaration::ImportDeclaration(import_decl) => { @@ -184,28 +185,28 @@ impl ModuleRecordBuilder { } } - fn visit_import_declaration(&mut self, decl: &ImportDeclaration) { - let module_request = NameSpan::new(decl.source.value.to_compact_str(), decl.source.span); + fn visit_import_declaration(&mut self, decl: &ImportDeclaration<'a>) { + let module_request = NameSpan::new(decl.source.value.clone(), decl.source.span); if let Some(specifiers) = &decl.specifiers { for specifier in specifiers { let (import_name, local_name, is_type) = match specifier { ImportDeclarationSpecifier::ImportSpecifier(specifier) => ( ImportImportName::Name(NameSpan::new( - specifier.imported.name().to_compact_str(), + specifier.imported.name(), specifier.imported.span(), )), - NameSpan::new(specifier.local.name.to_compact_str(), specifier.local.span), + NameSpan::new(specifier.local.name.clone(), specifier.local.span), decl.import_kind.is_type() || specifier.import_kind.is_type(), ), ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => ( ImportImportName::NamespaceObject, - NameSpan::new(specifier.local.name.to_compact_str(), specifier.local.span), + NameSpan::new(specifier.local.name.clone(), specifier.local.span), decl.import_kind.is_type(), ), ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => ( ImportImportName::Default(specifier.span), - NameSpan::new(specifier.local.name.to_compact_str(), specifier.local.span), + NameSpan::new(specifier.local.name.clone(), specifier.local.span), decl.import_kind.is_type(), ), }; @@ -224,8 +225,8 @@ impl ModuleRecordBuilder { ); } - fn visit_export_all_declaration(&mut self, decl: &ExportAllDeclaration) { - let module_request = NameSpan::new(decl.source.value.to_compact_str(), decl.source.span); + fn visit_export_all_declaration(&mut self, decl: &ExportAllDeclaration<'a>) { + let module_request = NameSpan::new(decl.source.value.clone(), decl.source.span); let export_entry = ExportEntry { module_request: Some(module_request.clone()), import_name: decl @@ -233,17 +234,14 @@ impl ModuleRecordBuilder { .as_ref() .map_or(ExportImportName::AllButDefault, |_| ExportImportName::All), export_name: decl.exported.as_ref().map_or(ExportExportName::Null, |exported_name| { - ExportExportName::Name(NameSpan::new( - exported_name.name().to_compact_str(), - exported_name.span(), - )) + ExportExportName::Name(NameSpan::new(exported_name.name(), exported_name.span())) }), span: decl.span, ..ExportEntry::default() }; self.add_export_entry(export_entry); if let Some(exported_name) = &decl.exported { - self.add_export_binding(exported_name.name().to_compact_str(), exported_name.span()); + self.add_export_binding(exported_name.name(), exported_name.span()); } self.add_module_request( &module_request, @@ -252,7 +250,7 @@ impl ModuleRecordBuilder { ); } - fn visit_export_default_declaration(&mut self, decl: &ExportDefaultDeclaration) { + fn visit_export_default_declaration(&mut self, decl: &ExportDefaultDeclaration<'a>) { // ignore all TypeScript syntax as they overload if decl.declaration.is_typescript_syntax() { return; @@ -263,17 +261,17 @@ impl ModuleRecordBuilder { let local_name = match &decl.declaration { ExportDefaultDeclarationKind::Identifier(ident) => { - ExportLocalName::Default(NameSpan::new(ident.name.to_compact_str(), ident.span)) + ExportLocalName::Default(NameSpan::new(ident.name.clone(), ident.span)) } ExportDefaultDeclarationKind::FunctionDeclaration(func) => { func.id.as_ref().map_or_else( || ExportLocalName::Null, - |id| ExportLocalName::Name(NameSpan::new(id.name.to_compact_str(), id.span)), + |id| ExportLocalName::Name(NameSpan::new(id.name.clone(), id.span)), ) } ExportDefaultDeclarationKind::ClassDeclaration(class) => class.id.as_ref().map_or_else( || ExportLocalName::Null, - |id| ExportLocalName::Name(NameSpan::new(id.name.to_compact_str(), id.span)), + |id| ExportLocalName::Name(NameSpan::new(id.name.clone(), id.span)), ), _ => ExportLocalName::Null, }; @@ -286,7 +284,7 @@ impl ModuleRecordBuilder { self.add_export_entry(export_entry); } - fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration) { + fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) { if decl.export_kind.is_type() { return; } @@ -295,10 +293,8 @@ impl ModuleRecordBuilder { return; } - let module_request = decl - .source - .as_ref() - .map(|source| NameSpan::new(source.value.to_compact_str(), source.span)); + let module_request = + decl.source.as_ref().map(|source| NameSpan::new(source.value.clone(), source.span)); if let Some(module_request) = &module_request { self.add_module_request( @@ -311,9 +307,9 @@ impl ModuleRecordBuilder { if let Some(decl) = &decl.declaration { decl.bound_names(&mut |ident| { let export_name = - ExportExportName::Name(NameSpan::new(ident.name.to_compact_str(), ident.span)); + ExportExportName::Name(NameSpan::new(ident.name.clone(), ident.span)); let local_name = - ExportLocalName::Name(NameSpan::new(ident.name.to_compact_str(), ident.span)); + ExportLocalName::Name(NameSpan::new(ident.name.clone(), ident.span)); let export_entry = ExportEntry { span: decl.span(), module_request: module_request.clone(), @@ -322,18 +318,18 @@ impl ModuleRecordBuilder { local_name, }; self.add_export_entry(export_entry); - self.add_export_binding(ident.name.to_compact_str(), ident.span); + self.add_export_binding(ident.name.clone(), ident.span); }); } for specifier in &decl.specifiers { let export_name = ExportExportName::Name(NameSpan::new( - specifier.exported.name().to_compact_str(), + specifier.exported.name().clone(), specifier.exported.span(), )); let import_name = if module_request.is_some() { ExportImportName::Name(NameSpan::new( - specifier.local.name().to_compact_str(), + specifier.local.name().clone(), specifier.local.span(), )) } else { @@ -343,7 +339,7 @@ impl ModuleRecordBuilder { ExportLocalName::Null } else { ExportLocalName::Name(NameSpan::new( - specifier.local.name().to_compact_str(), + specifier.local.name().clone(), specifier.local.span(), )) }; @@ -355,10 +351,7 @@ impl ModuleRecordBuilder { local_name, }; self.add_export_entry(export_entry); - self.add_export_binding( - specifier.exported.name().to_compact_str(), - specifier.exported.span(), - ); + self.add_export_binding(specifier.exported.name().clone(), specifier.exported.span()); } } } @@ -371,10 +364,9 @@ mod module_record_tests { use crate::Parser; - fn build(source_text: &str) -> ModuleRecord { + fn build<'a>(allocator: &'a Allocator, source_text: &'a str) -> ModuleRecord<'a> { let source_type = SourceType::mjs(); - let allocator = Allocator::default(); - let ret = Parser::new(&allocator, source_text, source_type).parse(); + let ret = Parser::new(allocator, source_text, source_type).parse(); ret.module_record } @@ -383,7 +375,8 @@ mod module_record_tests { #[test] fn import_default() { - let module_record = build("import v from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "import v from 'mod'"); let import_entry = ImportEntry { module_request: NameSpan::new("mod".into(), Span::new(14, 19)), import_name: ImportImportName::Default(Span::new(7, 8)), @@ -396,7 +389,8 @@ mod module_record_tests { #[test] fn import_namespace() { - let module_record = build("import * as ns from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "import * as ns from 'mod'"); let import_entry = ImportEntry { module_request: NameSpan::new("mod".into(), Span::new(20, 25)), import_name: ImportImportName::NamespaceObject, @@ -409,7 +403,8 @@ mod module_record_tests { #[test] fn import_specifier() { - let module_record = build("import { x } from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "import { x } from 'mod'"); let import_entry = ImportEntry { module_request: NameSpan::new("mod".into(), Span::new(18, 23)), import_name: ImportImportName::Name(NameSpan::new("x".into(), Span::new(9, 10))), @@ -422,7 +417,8 @@ mod module_record_tests { #[test] fn import_specifier_alias() { - let module_record = build("import { x as v } from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "import { x as v } from 'mod'"); let import_entry = ImportEntry { module_request: NameSpan::new("mod".into(), Span::new(23, 28)), import_name: ImportImportName::Name(NameSpan::new("x".into(), Span::new(9, 10))), @@ -435,7 +431,8 @@ mod module_record_tests { #[test] fn import_without_binding() { - let module_record = build("import 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "import 'mod'"); assert!(module_record.import_entries.is_empty()); } @@ -446,7 +443,8 @@ mod module_record_tests { fn export_star() { // ExportDeclaration : export ExportFromClause FromClause ; // ExportFromClause : * - let module_record = build("export * from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export * from 'mod'"); let export_entry = ExportEntry { module_request: Some(NameSpan::new("mod".into(), Span::new(14, 19))), import_name: ExportImportName::AllButDefault, @@ -461,7 +459,8 @@ mod module_record_tests { fn export_star_as_namespace() { // ExportDeclaration : export ExportFromClause FromClause ; // ExportFromClause : * as ModuleExportName - let module_record = build("export * as ns from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export * as ns from 'mod'"); let export_entry = ExportEntry { module_request: Some(NameSpan::new("mod".into(), Span::new(20, 25))), import_name: ExportImportName::All, @@ -477,7 +476,8 @@ mod module_record_tests { fn named_exports() { // ExportDeclaration : export NamedExports ; // ExportSpecifier : ModuleExportName - let module_record = build("export { x }"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export { x }"); let export_entry = ExportEntry { export_name: ExportExportName::Name(NameSpan::new("x".into(), Span::new(9, 10))), local_name: ExportLocalName::Name(NameSpan::new("x".into(), Span::new(9, 10))), @@ -492,7 +492,8 @@ mod module_record_tests { fn named_exports_alias() { // ExportDeclaration : export NamedExports ; // ExportSpecifier : ModuleExportName as ModuleExportName - let module_record = build("export { x as v }"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export { x as v }"); let export_entry = ExportEntry { export_name: ExportExportName::Name(NameSpan::new("v".into(), Span::new(14, 15))), local_name: ExportLocalName::Name(NameSpan::new("x".into(), Span::new(9, 10))), @@ -507,7 +508,8 @@ mod module_record_tests { fn named_exports_from() { // ExportDeclaration : export ExportFromClause FromClause ; // ExportSpecifier : ModuleExportName - let module_record = build("export { x } from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export { x } from 'mod'"); let export_entry = ExportEntry { module_request: Some(NameSpan::new("mod".into(), Span::new(18, 23))), export_name: ExportExportName::Name(NameSpan::new("x".into(), Span::new(9, 10))), @@ -523,7 +525,8 @@ mod module_record_tests { fn named_exports_alias_from() { // ExportDeclaration : export ExportFromClause FromClause ; // ExportSpecifier : ModuleExportName as ModuleExportName - let module_record = build("export { x as v } from 'mod'"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export { x as v } from 'mod'"); let export_entry = ExportEntry { module_request: Some(NameSpan::new("mod".into(), Span::new(23, 28))), export_name: ExportExportName::Name(NameSpan::new("v".into(), Span::new(14, 15))), @@ -538,7 +541,8 @@ mod module_record_tests { #[test] fn export_declaration() { // ExportDeclaration : export VariableStatement - let module_record = build("export var v"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export var v"); let export_entry = ExportEntry { export_name: ExportExportName::Name(NameSpan::new("v".into(), Span::new(11, 12))), local_name: ExportLocalName::Name(NameSpan::new("v".into(), Span::new(11, 12))), @@ -552,7 +556,8 @@ mod module_record_tests { #[test] fn export_default_declaration() { // ExportDeclaration : export default HoistableDeclaration - let module_record = build("export default function f() {}"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export default function f() {}"); let export_entry = ExportEntry { export_name: ExportExportName::Default(Span::new(7, 14)), local_name: ExportLocalName::Name(NameSpan::new("f".into(), Span::new(24, 25))), @@ -566,7 +571,8 @@ mod module_record_tests { #[test] fn export_default_function_expression() { // ExportDeclaration : export default HoistableDeclaration - let module_record = build("export default function() {}"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export default function() {}"); let export_entry = ExportEntry { export_name: ExportExportName::Default(Span::new(7, 14)), local_name: ExportLocalName::Null, @@ -580,7 +586,8 @@ mod module_record_tests { #[test] fn export_default_expression() { // ExportDeclaration : export default HoistableDeclaration - let module_record = build("export default 42"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export default 42"); let export_entry = ExportEntry { export_name: ExportExportName::Default(Span::new(7, 14)), local_name: ExportLocalName::Null, @@ -593,7 +600,8 @@ mod module_record_tests { #[test] fn export_named_default() { - let module_record = build("export { default }"); + let allocator = Allocator::default(); + let module_record = build(&allocator, "export { default }"); let export_entry = ExportEntry { export_name: ExportExportName::Name(NameSpan::new("default".into(), Span::new(9, 16))), local_name: ExportLocalName::Name(NameSpan::new("default".into(), Span::new(9, 16))), @@ -606,8 +614,9 @@ mod module_record_tests { #[test] fn indirect_export_entries() { + let allocator = Allocator::default(); let module_record = - build("import { x } from 'mod';export { x };export * as ns from 'mod';"); + build(&allocator, "import { x } from 'mod';export { x };export * as ns from 'mod';"); assert_eq!(module_record.indirect_export_entries.len(), 2); assert_eq!( module_record.indirect_export_entries[0], diff --git a/crates/oxc_syntax/Cargo.toml b/crates/oxc_syntax/Cargo.toml index bdc7dad7e6122..f1e22850f9033 100644 --- a/crates/oxc_syntax/Cargo.toml +++ b/crates/oxc_syntax/Cargo.toml @@ -28,7 +28,6 @@ oxc_span = { workspace = true } assert-unchecked = { workspace = true } bitflags = { workspace = true } -dashmap = { workspace = true } nonmax = { workspace = true } phf = { workspace = true, features = ["macros"] } rustc-hash = { workspace = true } diff --git a/crates/oxc_syntax/src/lib.rs b/crates/oxc_syntax/src/lib.rs index b1efc6889c05b..94b728ff74dd8 100644 --- a/crates/oxc_syntax/src/lib.rs +++ b/crates/oxc_syntax/src/lib.rs @@ -3,7 +3,6 @@ pub mod class; pub mod identifier; pub mod keyword; -pub mod module_graph_visitor; pub mod module_record; pub mod node; pub mod number; diff --git a/crates/oxc_syntax/src/module_record.rs b/crates/oxc_syntax/src/module_record.rs index a2aaf61a2c837..f1f6d91d3afcf 100644 --- a/crates/oxc_syntax/src/module_record.rs +++ b/crates/oxc_syntax/src/module_record.rs @@ -1,13 +1,12 @@ //! [ECMAScript Module Record](https://tc39.es/ecma262/#sec-abstract-module-records) #![allow(missing_docs)] // fixme -use std::{fmt, path::PathBuf, sync::Arc}; +use std::fmt; -use dashmap::DashMap; -use oxc_span::{CompactStr, Span}; -use rustc_hash::{FxBuildHasher, FxHashMap}; +use oxc_allocator::{Allocator, Vec}; +use oxc_span::{Atom, Span}; -type FxDashMap = DashMap; +use rustc_hash::FxHashMap; /// ESM Module Record /// @@ -16,14 +15,10 @@ type FxDashMap = DashMap; /// See /// * /// * -#[derive(Default)] -pub struct ModuleRecord { +pub struct ModuleRecord<'a> { /// This module has no import / export statements pub not_esm: bool, - /// Resolved absolute path to this module record - pub resolved_absolute_path: PathBuf, - /// `[[RequestedModules]]` /// /// A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. The List is in source text occurrence order. @@ -33,82 +28,73 @@ pub struct ModuleRecord { /// import ModuleSpecifier /// export ExportFromClause FromClause /// Keyed by ModuleSpecifier, valued by all node occurrences - pub requested_modules: FxHashMap>, - - /// `[[LoadedModules]]` - /// - /// A map from the specifier strings used by the module represented by this record to request - /// the importation of a module to the resolved Module Record. The list does not contain two - /// different Records with the same `[[Specifier]]`. - /// - /// Note that Oxc does not support cross-file analysis, so this map will be empty after - /// [`ModuleRecord`] is created. You must link the module records yourself. - pub loaded_modules: FxDashMap>, + pub requested_modules: FxHashMap, Vec<'a, RequestedModule>>, /// `[[ImportEntries]]` /// /// A List of ImportEntry records derived from the code of this module - pub import_entries: Vec, + pub import_entries: Vec<'a, ImportEntry<'a>>, /// `[[LocalExportEntries]]` /// /// A List of [`ExportEntry`] records derived from the code of this module /// that correspond to declarations that occur within the module - pub local_export_entries: Vec, + pub local_export_entries: Vec<'a, ExportEntry<'a>>, /// `[[IndirectExportEntries]]` /// /// A List of [`ExportEntry`] records derived from the code of this module /// that correspond to reexported imports that occur within the module /// or exports from `export * as namespace` declarations. - pub indirect_export_entries: Vec, + pub indirect_export_entries: Vec<'a, ExportEntry<'a>>, /// `[[StarExportEntries]]` /// /// A List of [`ExportEntry`] records derived from the code of this module /// that correspond to `export *` declarations that occur within the module, /// not including `export * as namespace` declarations. - pub star_export_entries: Vec, + pub star_export_entries: Vec<'a, ExportEntry<'a>>, /// Local exported bindings - pub exported_bindings: FxHashMap, + pub exported_bindings: FxHashMap, Span>, /// Local duplicated exported bindings, for diagnostics - pub exported_bindings_duplicated: Vec, + pub exported_bindings_duplicated: Vec<'a, NameSpan<'a>>, /// Reexported bindings from `export * from 'specifier'` /// Keyed by resolved path - pub exported_bindings_from_star_export: FxDashMap>, + pub exported_bindings_from_star_export: FxHashMap, Vec<'a, Atom<'a>>>, /// `export default name` /// ^^^^^^^ span pub export_default: Option, /// Duplicated span of `export default` for diagnostics - pub export_default_duplicated: Vec, + pub export_default_duplicated: Vec<'a, Span>, } -impl ModuleRecord { - pub fn new(resolved_absolute_path: PathBuf) -> Self { - Self { resolved_absolute_path, ..Self::default() } +impl<'a> ModuleRecord<'a> { + pub fn new(allocator: &'a Allocator) -> Self { + Self { + not_esm: true, + requested_modules: FxHashMap::default(), + import_entries: Vec::new_in(allocator), + local_export_entries: Vec::new_in(allocator), + indirect_export_entries: Vec::new_in(allocator), + star_export_entries: Vec::new_in(allocator), + exported_bindings: FxHashMap::default(), + exported_bindings_duplicated: Vec::new_in(allocator), + exported_bindings_from_star_export: FxHashMap::default(), + export_default: None, + export_default_duplicated: Vec::new_in(allocator), + } } } -impl fmt::Debug for ModuleRecord { +impl fmt::Debug for ModuleRecord<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { - // recursively formatting loaded modules can crash when the module graph is cyclic - let loaded_modules = self - .loaded_modules - .iter() - .map(|entry| (entry.key().to_string())) - .reduce(|acc, key| format!("{acc}, {key}")) - .unwrap_or_default(); - let loaded_modules = format!("{{ {loaded_modules} }}"); f.debug_struct("ModuleRecord") .field("not_esm", &self.not_esm) - .field("resolved_absolute_path", &self.resolved_absolute_path) - .field("requested_modules", &self.requested_modules) - .field("loaded_modules", &loaded_modules) .field("import_entries", &self.import_entries) .field("local_export_entries", &self.local_export_entries) .field("indirect_export_entries", &self.indirect_export_entries) @@ -123,23 +109,15 @@ impl fmt::Debug for ModuleRecord { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct NameSpan { - name: CompactStr, - span: Span, +pub struct NameSpan<'a> { + pub name: Atom<'a>, + pub span: Span, } -impl NameSpan { - pub fn new(name: CompactStr, span: Span) -> Self { +impl<'a> NameSpan<'a> { + pub fn new(name: Atom<'a>, span: Span) -> Self { Self { name, span } } - - pub const fn name(&self) -> &CompactStr { - &self.name - } - - pub const fn span(&self) -> Span { - self.span - } } /// [`ImportEntry`](https://tc39.es/ecma262/#importentry-record) @@ -158,7 +136,7 @@ impl NameSpan { /// import * as ns from "mod"; /// ``` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ImportEntry { +pub struct ImportEntry<'a> { /// String value of the ModuleSpecifier of the ImportDeclaration. /// /// ## Examples @@ -167,7 +145,7 @@ pub struct ImportEntry { /// import { foo } from "mod"; /// // ^^^ /// ``` - pub module_request: NameSpan, + pub module_request: NameSpan<'a>, /// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`. /// @@ -179,7 +157,7 @@ pub struct ImportEntry { /// import { foo as bar } from "mod"; /// // ^^^ /// ``` - pub import_name: ImportImportName, + pub import_name: ImportImportName<'a>, /// The name that is used to locally access the imported value from within the importing module. /// @@ -191,7 +169,7 @@ pub struct ImportEntry { /// import { foo as bar } from "mod"; /// // ^^^ /// ``` - pub local_name: NameSpan, + pub local_name: NameSpan<'a>, /// Whether this binding is for a TypeScript type-only import. This is a non-standard field. /// When creating a [`ModuleRecord`] for a JavaScript file, this will always be false. @@ -214,13 +192,13 @@ pub struct ImportEntry { /// `ImportName` For `ImportEntry` #[derive(Debug, Clone, PartialEq, Eq)] -pub enum ImportImportName { - Name(NameSpan), +pub enum ImportImportName<'a> { + Name(NameSpan<'a>), NamespaceObject, Default(Span), } -impl ImportImportName { +impl ImportImportName<'_> { pub fn is_default(&self) -> bool { matches!(self, Self::Default(_)) } @@ -250,32 +228,32 @@ impl ImportImportName { /// /// ``` #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct ExportEntry { +pub struct ExportEntry<'a> { /// Span for the entire export entry pub span: Span, /// The String value of the ModuleSpecifier of the ExportDeclaration. /// null if the ExportDeclaration does not have a ModuleSpecifier. - pub module_request: Option, + pub module_request: Option>, /// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`. /// null if the ExportDeclaration does not have a ModuleSpecifier. /// "all" is used for `export * as ns from "mod"`` declarations. /// "all-but-default" is used for `export * from "mod" declarations`. - pub import_name: ExportImportName, + pub import_name: ExportImportName<'a>, /// The name used to export this binding by this module. - pub export_name: ExportExportName, + pub export_name: ExportExportName<'a>, /// The name that is used to locally access the exported value from within the importing module. /// null if the exported value is not locally accessible from within the module. - pub local_name: ExportLocalName, + pub local_name: ExportLocalName<'a>, } /// `ImportName` for `ExportEntry` #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub enum ExportImportName { - Name(NameSpan), +pub enum ExportImportName<'a> { + Name(NameSpan<'a>), /// all is used for export * as ns from "mod" declarations. All, /// all-but-default is used for export * from "mod" declarations. @@ -285,7 +263,7 @@ pub enum ExportImportName { Null, } -impl ExportImportName { +impl ExportImportName<'_> { pub fn is_all(&self) -> bool { matches!(self, Self::All) } @@ -297,14 +275,14 @@ impl ExportImportName { /// `ExportName` for `ExportEntry` #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub enum ExportExportName { - Name(NameSpan), +pub enum ExportExportName<'a> { + Name(NameSpan<'a>), Default(Span), #[default] Null, } -impl ExportExportName { +impl ExportExportName<'_> { /// Returns `true` if this is [`ExportExportName::Default`]. pub fn is_default(&self) -> bool { matches!(self, Self::Default(_)) @@ -318,7 +296,7 @@ impl ExportExportName { /// Attempt to get the [`Span`] of this export name. pub fn span(&self) -> Option { match self { - Self::Name(name) => Some(name.span()), + Self::Name(name) => Some(name.span), Self::Default(span) => Some(*span), Self::Null => None, } @@ -327,15 +305,15 @@ impl ExportExportName { /// `LocalName` for `ExportEntry` #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub enum ExportLocalName { - Name(NameSpan), +pub enum ExportLocalName<'a> { + Name(NameSpan<'a>), /// `export default name_span` - Default(NameSpan), + Default(NameSpan<'a>), #[default] Null, } -impl ExportLocalName { +impl<'a> ExportLocalName<'a> { /// `true` if this is a [`ExportLocalName::Default`]. pub fn is_default(&self) -> bool { matches!(self, Self::Default(_)) @@ -347,19 +325,15 @@ impl ExportLocalName { } /// Get the bound name of this export. [`None`] for [`ExportLocalName::Null`]. - pub const fn name(&self) -> Option<&CompactStr> { + pub fn name(&self) -> Option<&Atom<'a>> { match self { - Self::Name(name) | Self::Default(name) => Some(name.name()), + Self::Name(name) | Self::Default(name) => Some(&name.name), Self::Null => None, } } } -pub struct FunctionMeta { - pub deprecated: bool, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct RequestedModule { span: Span, is_type: bool, diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 84d8764d78244..6bd7a08a097f3 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -21,11 +21,10 @@ use oxc::{ ScopeFlags, ScopeId, ScopeTree, SemanticBuilder, SymbolTable, }, span::SourceType, - syntax::module_record::ModuleRecord, transformer::{TransformOptions, Transformer}, }; use oxc_index::Idx; -use oxc_linter::Linter; +use oxc_linter::{Linter, ModuleRecord}; use oxc_prettier::{Prettier, PrettierOptions}; use serde::Serialize; use tsify::Tsify; @@ -229,7 +228,7 @@ impl Oxc { ); } - let module_record = Arc::new(module_record); + let module_record = Arc::new(ModuleRecord::new(&path, &module_record)); self.run_linter(&run_options, &path, &program, &module_record); self.run_prettier(&run_options, source_text, source_type); diff --git a/tasks/benchmark/benches/linter.rs b/tasks/benchmark/benches/linter.rs index e4d5305650788..67daf08687d86 100644 --- a/tasks/benchmark/benches/linter.rs +++ b/tasks/benchmark/benches/linter.rs @@ -2,7 +2,7 @@ use std::{env, path::Path, rc::Rc, sync::Arc}; use oxc_allocator::Allocator; use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use oxc_linter::{FixKind, LinterBuilder}; +use oxc_linter::{FixKind, LinterBuilder, ModuleRecord}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; @@ -39,7 +39,7 @@ 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); - let module_record = Arc::new(ret.module_record); + let module_record = Arc::new(ModuleRecord::new(path, &ret.module_record)); b.iter(|| linter.run(path, Rc::clone(&semantic), Arc::clone(&module_record))); }, );