Skip to content

Commit

Permalink
feat(parser)!: Build ModuleRecord directly in parser (#7546)
Browse files Browse the repository at this point in the history
This has the benefit of:

* expose dynamic import / import meta info from parser
* 1 less ast shallow in semantic builder
* no ast walk in oxc's module lexer
* some more benefits coming soon
  • Loading branch information
Boshen committed Nov 29, 2024
1 parent 5c4488c commit 8a788b8
Show file tree
Hide file tree
Showing 26 changed files with 598 additions and 611 deletions.
5 changes: 2 additions & 3 deletions crates/oxc/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ pub trait CompilerInterface {

/* Semantic */

let mut semantic_return = self.semantic(&program, source_path);
let mut semantic_return = self.semantic(&program);
if !semantic_return.errors.is_empty() {
self.handle_errors(semantic_return.errors);
return;
Expand Down Expand Up @@ -231,7 +231,7 @@ pub trait CompilerInterface {
Parser::new(allocator, source_text, source_type).with_options(self.parse_options()).parse()
}

fn semantic<'a>(&self, program: &Program<'a>, source_path: &Path) -> SemanticBuilderReturn<'a> {
fn semantic<'a>(&self, program: &Program<'a>) -> SemanticBuilderReturn<'a> {
let mut builder = SemanticBuilder::new();

if self.transform_options().is_some() {
Expand All @@ -242,7 +242,6 @@ pub trait CompilerInterface {
builder
.with_check_syntax_error(self.check_semantic_error())
.with_scope_tree_child_ids(self.semantic_child_scope_ids())
.build_module_record(source_path, program)
.build(program)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/frameworks.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{hash, path::Path};

use bitflags::bitflags;
use oxc_semantic::ModuleRecord;
use oxc_syntax::module_record::ModuleRecord;

bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down
5 changes: 3 additions & 2 deletions crates/oxc_linter/src/rules/import/export.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::path::PathBuf;

use rustc_hash::{FxHashMap, FxHashSet};

use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
use oxc_macros::declare_oxc_lint;
use oxc_semantic::ModuleRecord;
use oxc_span::{CompactStr, Span};
use rustc_hash::{FxHashMap, FxHashSet};
use oxc_syntax::module_record::ModuleRecord;

use crate::{context::LintContext, rule::Rule};

Expand Down
6 changes: 4 additions & 2 deletions crates/oxc_linter/src/rules/import/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use oxc_ast::{
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{AstNode, ModuleRecord};
use oxc_semantic::AstNode;
use oxc_span::{GetSpan, Span};
use oxc_syntax::module_record::{ExportExportName, ExportImportName, ImportImportName};
use oxc_syntax::module_record::{
ExportExportName, ExportImportName, ImportImportName, ModuleRecord,
};

use crate::{context::LintContext, rule::Rule};

Expand Down
6 changes: 4 additions & 2 deletions crates/oxc_linter/src/rules/oxc/no_barrel_file.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
use oxc_macros::declare_oxc_lint;
use oxc_semantic::ModuleRecord;
use oxc_syntax::module_graph_visitor::{ModuleGraphVisitorBuilder, VisitFoldWhile};
use oxc_syntax::{
module_graph_visitor::{ModuleGraphVisitorBuilder, VisitFoldWhile},
module_record::ModuleRecord,
};

use crate::{context::LintContext, rule::Rule};

Expand Down
5 changes: 3 additions & 2 deletions crates/oxc_linter/src/service/module_cache.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::{
num::NonZeroUsize,
path::Path,
sync::{Arc, Condvar, Mutex},
};

use dashmap::{mapref::one::Ref, DashMap};
use oxc_semantic::ModuleRecord;
use rustc_hash::{FxBuildHasher, FxHashMap};
use std::num::NonZeroUsize;

use oxc_syntax::module_record::ModuleRecord;

type FxDashMap<K, V> = DashMap<K, V, FxBuildHasher>;

Expand Down
12 changes: 8 additions & 4 deletions crates/oxc_linter/src/service/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use oxc_allocator::Allocator;
use oxc_diagnostics::{DiagnosticSender, DiagnosticService, Error, OxcDiagnostic};
use oxc_parser::{ParseOptions, Parser};
use oxc_resolver::Resolver;
use oxc_semantic::{ModuleRecord, SemanticBuilder};
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;

Expand Down Expand Up @@ -215,9 +216,11 @@ impl Runtime {
.with_cfg(true)
.with_scope_tree_child_ids(true)
.with_build_jsdoc(true)
.with_check_syntax_error(check_syntax_errors)
.build_module_record(path, &ret.program);
let module_record = semantic_builder.module_record();
.with_check_syntax_error(check_syntax_errors);

let mut module_record = ret.module_record;
module_record.resolved_absolute_path = path.to_path_buf();
let module_record = Arc::new(module_record);

if self.resolver.is_some() {
self.modules.add_resolved_module(path, Arc::clone(&module_record));
Expand Down Expand Up @@ -294,6 +297,7 @@ 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))
}
Expand Down
1 change: 0 additions & 1 deletion crates/oxc_module_lexer/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ pub fn parse(source: &str) -> ModuleLexer {
let allocator = Allocator::default();
let source_type = SourceType::mjs();
let ret = Parser::new(&allocator, source, source_type).parse();
assert!(ret.errors.is_empty(), "{source} should not produce errors.\n{:?}", ret.errors);
let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program);
// Copy data over because `ModuleLexer<'a>` can't be returned
ModuleLexer {
Expand Down
8 changes: 8 additions & 0 deletions crates/oxc_parser/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,14 @@ pub fn cover_initialized_name(span: Span) -> OxcDiagnostic {
.with_label(span)
}

#[cold]
pub fn duplicate_export(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic {
OxcDiagnostic::error(format!("Duplicated export '{x0}'")).with_labels([
span1.label("Export has already been declared here"),
span2.label("It cannot be redeclared here"),
])
}

// ================================= MODIFIERS =================================

#[cold]
Expand Down
7 changes: 5 additions & 2 deletions crates/oxc_parser/src/js/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ impl<'a> ParserImpl<'a> {
}
let stmt = self.parse_statement_list_item(StatementContext::StatementList)?;

if is_top_level && stmt.is_module_declaration() {
self.set_source_type_to_module_if_unambiguous();
if is_top_level {
if let Some(module_decl) = stmt.as_module_declaration() {
self.set_source_type_to_module_if_unambiguous();
self.module_record_builder.visit_module_declaration(module_decl);
}
}

// Section 11.2.1 Directive Prologue
Expand Down
27 changes: 25 additions & 2 deletions crates/oxc_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
mod context;
mod cursor;
mod modifiers;
mod module_record;
mod state;

mod js;
Expand All @@ -84,17 +85,19 @@ mod lexer;
#[doc(hidden)]
pub mod lexer;

use context::{Context, StatementContext};
use oxc_allocator::{Allocator, Box as ArenaBox};
use oxc_ast::{
ast::{Expression, Program},
AstBuilder,
};
use oxc_diagnostics::{OxcDiagnostic, Result};
use oxc_span::{ModuleKind, SourceType, Span};
use oxc_syntax::module_record::ModuleRecord;

use crate::{
context::{Context, StatementContext},
lexer::{Kind, Lexer, Token},
module_record::ModuleRecordBuilder,
state::ParserState,
};

Expand Down Expand Up @@ -150,6 +153,9 @@ pub struct ParserReturn<'a> {
/// semantic analysis with syntax error checking enabled.
pub program: Program<'a>,

/// See <https://tc39.es/ecma262/#sec-abstract-module-records>
pub module_record: ModuleRecord,

/// Syntax errors encountered while parsing.
///
/// This list is not comprehensive. Oxc offloads more-expensive checks to [semantic
Expand Down Expand Up @@ -363,6 +369,9 @@ struct ParserImpl<'a> {
/// Ast builder for creating AST nodes
ast: AstBuilder<'a>,

/// Module Record Builder
module_record_builder: ModuleRecordBuilder,

/// Precomputed typescript detection
is_ts: bool,
}
Expand All @@ -380,6 +389,8 @@ 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),
Expand All @@ -391,6 +402,7 @@ impl<'a> ParserImpl<'a> {
state: ParserState::default(),
ctx: Self::default_context(source_type, options),
ast: AstBuilder::new(allocator),
module_record_builder,
is_ts: source_type.is_typescript(),
}
}
Expand Down Expand Up @@ -432,10 +444,21 @@ impl<'a> ParserImpl<'a> {
errors.reserve(self.lexer.errors.len() + self.errors.len());
errors.extend(self.lexer.errors);
errors.extend(self.errors);
// Skip checking for exports in TypeScript {
if !self.source_type.is_typescript() {
errors.extend(self.module_record_builder.errors());
}
}
let irregular_whitespaces =
self.lexer.trivia_builder.irregular_whitespaces.into_boxed_slice();
ParserReturn { program, errors, irregular_whitespaces, panicked, is_flow_language }
ParserReturn {
program,
module_record: self.module_record_builder.build(),
errors,
irregular_whitespaces,
panicked,
is_flow_language,
}
}

pub fn parse_expression(mut self) -> std::result::Result<Expression<'a>, Vec<OxcDiagnostic>> {
Expand Down
Loading

0 comments on commit 8a788b8

Please sign in to comment.