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
refactor(linter): access scoping from ctx directly (#9624)
  • Loading branch information
Boshen committed Mar 9, 2025
commit 03a40df31b9fd58646c963351a745529d04a6c53
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl<'a> LintContext<'a> {
pub fn cfg(&self) -> &ControlFlowGraph {
// SAFETY: `LintContext::new` is the only way to construct a `LintContext` and we always
// assert the existence of control flow so it should always be `Some`.
unsafe { self.semantic().cfg().unwrap_unchecked() }
unsafe { self.parent.semantic.cfg().unwrap_unchecked() }
}

/// List of all disable directives in the file being linted.
Expand All @@ -131,7 +131,7 @@ impl<'a> LintContext<'a> {
/// Get a snippet of source text covered by the given [`Span`]. For details,
/// see [`Span::source_text`].
pub fn source_range(&self, span: Span) -> &'a str {
span.source_text(self.semantic().source_text())
span.source_text(self.parent.semantic.source_text())
}

/// Path to the file currently being linted.
Expand Down
5 changes: 2 additions & 3 deletions crates/oxc_linter/src/rules/eslint/max_classes_per_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,10 @@ impl Rule for MaxClassesPerFile {
}

fn run_once(&self, ctx: &LintContext<'_>) {
let mut class_count = ctx.semantic().classes().declarations.len();
let mut class_count = ctx.classes().declarations.len();

if self.ignore_expressions {
let class_expressions = ctx
.semantic()
.classes()
.iter_enumerated()
.filter(|(_class_id, node_id)| !ctx.nodes().kind(**node_id).is_declaration())
Expand All @@ -99,7 +98,7 @@ impl Rule for MaxClassesPerFile {
return;
}

let node_id = ctx.semantic().classes().get_node_id(ClassId::from(self.max));
let node_id = ctx.classes().get_node_id(ClassId::from(self.max));
let span = if let AstKind::Class(class) = ctx.nodes().kind(node_id) {
class.span
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,7 @@ impl Rule for MaxLinesPerFunction {
let source_text = ctx.source_text();

let comment_lines = if self.skip_comments {
ctx.semantic()
.comments_range(span.start..span.end)
ctx.comments_range(span.start..span.end)
.map(|comment| count_comment_lines(comment, source_text))
.sum()
} else {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_await_in_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Rule for NoAwaitInLoop {
_ => return,
};

let nodes = ctx.semantic().nodes();
let nodes = ctx.nodes();
// Perform validation for AwaitExpression and ForOfStatement that contains await
let mut parent_node = nodes.parent_node(node.id());
let mut is_in_loop = false;
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_class_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ declare_oxc_lint!(

impl Rule for NoClassAssign {
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
let symbol_table = ctx.semantic().scoping();
let symbol_table = ctx.scoping();
if symbol_table.symbol_flags(symbol_id).is_class() {
for reference in symbol_table.get_resolved_references(symbol_id) {
if reference.is_write() {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_const_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ declare_oxc_lint!(

impl Rule for NoConstAssign {
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
let symbol_table = ctx.semantic().scoping();
let symbol_table = ctx.scoping();
if symbol_table.symbol_flags(symbol_id).is_const_variable() {
for reference in symbol_table.get_resolved_references(symbol_id) {
if reference.is_write() {
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/eslint/no_dupe_class_members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ declare_oxc_lint!(

impl Rule for NoDupeClassMembers {
fn run_once(&self, ctx: &LintContext) {
ctx.semantic().classes().iter_enumerated().for_each(|(class_id, _)| {
ctx.classes().iter_enumerated().for_each(|(class_id, _)| {
let mut defined_elements = FxHashMap::default();
let elements = &ctx.semantic().classes().elements[class_id];
let elements = &ctx.classes().elements[class_id];
for (element_id, element) in elements.iter_enumerated() {
if let Some(prev_element_id) = defined_elements.insert(&element.name, element_id) {
let prev_element = &elements[prev_element_id];
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl Rule for NoEmpty {
return;
}

if ctx.semantic().has_comments_between(block.span) {
if ctx.has_comments_between(block.span) {
return;
}
ctx.diagnostic_with_suggestion(no_empty_diagnostic("block", block.span), |fixer| {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_empty_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Rule for NoEmptyFunction {
let AstKind::FunctionBody(fb) = node.kind() else {
return;
};
if fb.is_empty() && !ctx.semantic().has_comments_between(fb.span) {
if fb.is_empty() && !ctx.has_comments_between(fb.span) {
let (kind, fn_name) = get_function_name_and_kind(node, ctx);
ctx.diagnostic(no_empty_function_diagnostic(fb.span, kind, fn_name));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Rule for NoEmptyStaticBlock {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::StaticBlock(static_block) = node.kind() {
if static_block.body.is_empty() {
if ctx.semantic().has_comments_between(static_block.span) {
if ctx.has_comments_between(static_block.span) {
return;
}
ctx.diagnostic_with_suggestion(
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_ex_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ declare_oxc_lint!(

impl Rule for NoExAssign {
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
let symbol_table = ctx.semantic().scoping();
let symbol_table = ctx.scoping();
if symbol_table.symbol_flags(symbol_id).is_catch_variable() {
for reference in symbol_table.get_resolved_references(symbol_id) {
if reference.is_write() {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_func_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ declare_oxc_lint!(

impl Rule for NoFuncAssign {
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
let symbol_table = ctx.semantic().scoping();
let symbol_table = ctx.scoping();
let decl = symbol_table.get_symbol_declaration(symbol_id);
if let AstKind::Function(_) = ctx.nodes().kind(decl) {
for reference in symbol_table.get_resolved_references(symbol_id) {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_import_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const REFLECT_MUTATION_METHODS: phf::Set<&'static str> =

impl Rule for NoImportAssign {
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
let symbol_table = ctx.semantic().scoping();
let symbol_table = ctx.scoping();
if symbol_table.symbol_flags(symbol_id).is_import() {
let kind = ctx.nodes().kind(symbol_table.get_symbol_declaration(symbol_id));
let is_namespace_specifier = matches!(kind, AstKind::ImportNamespaceSpecifier(_));
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_lone_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl Rule for NoLoneBlocks {

if stmt.body.is_empty() {
let is_comment_in_stmt =
ctx.semantic().comments_range(stmt.span.start..stmt.span.end).last().is_some();
ctx.comments_range(stmt.span.start..stmt.span.end).last().is_some();

if !is_comment_in_stmt
&& !matches!(parent_node.kind(), AstKind::TryStatement(_) | AstKind::CatchClause(_))
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_magic_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ impl Rule for NoMagicNumbers {
return;
}

let nodes = ctx.semantic().nodes();
let nodes = ctx.nodes();
let config = InternConfig::from(node, nodes.parent_node(node.id()).unwrap());

if self.is_skipable(&config, nodes) {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_redeclare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl Rule for NoRedeclare {
}

fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext) {
let symbol_table = ctx.semantic().scoping();
let symbol_table = ctx.scoping();
let decl_node_id = symbol_table.get_symbol_declaration(symbol_id);
match ctx.nodes().kind(decl_node_id) {
AstKind::VariableDeclarator(var) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl Rule for NoShadowRestrictedNames {

if name == "undefined" {
// Allow to declare `undefined` variable but not allow to assign value to it.
let node_id = ctx.semantic().scoping().get_symbol_declaration(symbol_id);
let node_id = ctx.scoping().get_symbol_declaration(symbol_id);
if let AstKind::VariableDeclarator(declarator) = ctx.nodes().kind(node_id) {
if declarator.init.is_none()
&& ctx
Expand Down
5 changes: 2 additions & 3 deletions crates/oxc_linter/src/rules/eslint/no_this_before_super.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,13 @@ enum DefinitelyCallsThisBeforeSuper {
impl Rule for NoThisBeforeSuper {
fn run_once(&self, ctx: &LintContext) {
let cfg = ctx.cfg();
let semantic = ctx.semantic();

// first pass -> find super calls and local violations
let mut wanted_nodes = Vec::new();
let mut basic_blocks_with_super_called = FxHashSet::<BlockNodeId>::default();
let mut basic_blocks_with_local_violations =
FxHashMap::<BlockNodeId, Vec<NodeId>>::default();
for node in semantic.nodes() {
for node in ctx.nodes() {
match node.kind() {
AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) => {
if Self::is_wanted_node(node, ctx).unwrap_or_default() {
Expand All @@ -73,7 +72,7 @@ impl Rule for NoThisBeforeSuper {
}
AstKind::Super(_) => {
let basic_block_id = node.cfg_id();
if let Some(parent) = semantic.nodes().parent_node(node.id()) {
if let Some(parent) = ctx.nodes().parent_node(node.id()) {
if let AstKind::CallExpression(call_expr) = parent.kind() {
let has_this_or_super_in_args =
Self::contains_this_or_super_in_args(&call_expr.arguments);
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/eslint/no_unused_labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ declare_oxc_lint!(

impl Rule for NoUnusedLabels {
fn run_once(&self, ctx: &LintContext) {
for id in ctx.semantic().unused_labels() {
let node = ctx.semantic().nodes().get_node(*id);
for id in ctx.unused_labels() {
let node = ctx.nodes().get_node(*id);
let AstKind::LabeledStatement(stmt) = node.kind() else {
continue;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,15 @@ declare_oxc_lint!(

impl Rule for NoUnusedPrivateClassMembers {
fn run_once(&self, ctx: &LintContext) {
ctx.semantic().classes().iter_enumerated().for_each(|(class_id, _)| {
for (element_id, element) in
ctx.semantic().classes().elements[class_id].iter_enumerated()
{
ctx.classes().iter_enumerated().for_each(|(class_id, _)| {
for (element_id, element) in ctx.classes().elements[class_id].iter_enumerated() {
if !element.kind.intersects(ElementKind::Property | ElementKind::Method) {
continue;
}
if element.is_private
&& !ctx.semantic().classes().iter_private_identifiers(class_id).any(|ident| {
&& !ctx.classes().iter_private_identifiers(class_id).any(|ident| {
// If the element is a property, it must be read.
(!element.kind.is_property() || is_read(ident.id, ctx.semantic().nodes()))
(!element.kind.is_property() || is_read(ident.id, ctx.nodes()))
&& ident.element_ids.contains(&element_id)
})
{
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_unused_vars/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl Rule for NoUnusedVars {
}

fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
let symbol = Symbol::new(ctx.semantic().as_ref(), ctx.module_record(), symbol_id);
let symbol = Symbol::new(ctx, ctx.module_record(), symbol_id);
if Self::should_skip_symbol(&symbol) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/sort_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ impl SortImports {
// import { /* comment */ a, b, c, d } from 'foo.js'
// ```
// I use ImportStatement's span to check if there are comments between the specifiers.
let is_fixable = !ctx.semantic().has_comments_between(current.span);
let is_fixable = !ctx.has_comments_between(current.span);

if is_fixable {
// Safe to index because we know that `specifiers` is at least 2 element long
Expand Down
4 changes: 1 addition & 3 deletions crates/oxc_linter/src/rules/import/named.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,8 @@ declare_oxc_lint!(

impl Rule for Named {
fn run_once(&self, ctx: &LintContext<'_>) {
let semantic = ctx.semantic();

// This rule is disabled in the typescript config.
if semantic.source_type().is_typescript() {
if ctx.source_type().is_typescript() {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl Rule for NoNamedAsDefaultMember {
};
};

for item in ctx.semantic().nodes() {
for item in ctx.nodes() {
match item.kind() {
AstKind::MemberExpression(member_expr) => process_member_expr(member_expr),
AstKind::VariableDeclarator(decl) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl Rule for NoCommentedOutTests {
static ref RE: Regex =
Regex::new(r#"(?mu)^\s*[xf]?(test|it|describe)(\.\w+|\[['"]\w+['"]\])?\s*\("#).unwrap();
}
let comments = ctx.semantic().comments();
let comments = ctx.comments();
let commented_tests = comments.iter().filter_map(|comment| {
let text = ctx.source_range(comment.content_span());
if RE.is_match(text) { Some(comment.content_span()) } else { None }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ fn check_parents<'a>(
let Some(ident) = &function.id else {
return InConditional(false);
};
let symbol_table = ctx.semantic().scoping();
let symbol_table = ctx.scoping();
let symbol_id = ident.symbol_id();

// Consider cases like:
Expand Down
7 changes: 2 additions & 5 deletions crates/oxc_linter/src/rules/jsdoc/check_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,8 @@ impl Rule for CheckAccess {
access_related_tag_names.insert(settings.resolve_tag_name(level));
}

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
for jsdoc in
ctx.jsdoc().iter_all().filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
{
let mut access_related_tags_count = 0;
for tag in jsdoc.tags() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ impl Rule for CheckPropertyNames {
let resolved_property_tag_name = settings.resolve_tag_name("property");

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
Expand Down
1 change: 0 additions & 1 deletion crates/oxc_linter/src/rules/jsdoc/check_tag_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ impl Rule for CheckTagNames {
let is_ambient = is_dts || is_declare;

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
Expand Down
7 changes: 2 additions & 5 deletions crates/oxc_linter/src/rules/jsdoc/empty_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,8 @@ impl Rule for EmptyTags {
false
};

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_private(jsdoc, settings))
for jsdoc in
ctx.jsdoc().iter_all().filter(|jsdoc| !should_ignore_as_private(jsdoc, settings))
{
for tag in jsdoc.tags() {
let tag_name = tag.kind.parsed();
Expand Down
1 change: 0 additions & 1 deletion crates/oxc_linter/src/rules/jsdoc/require_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ impl Rule for RequireProperty {
let resolved_namespace_tag_name = settings.resolve_tag_name("namespace");

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ impl Rule for RequirePropertyDescription {
let resolved_property_tag_name = settings.resolve_tag_name("property");

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ impl Rule for RequirePropertyName {
let resolved_property_tag_name = settings.resolve_tag_name("property");

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ impl Rule for RequirePropertyType {
let resolved_property_tag_name = settings.resolve_tag_name("property");

for jsdoc in ctx
.semantic()
.jsdoc()
.iter_all()
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
Expand Down
6 changes: 3 additions & 3 deletions crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,22 @@ impl Rule for NoAccumulatingSpread {
return;
};

let symbols = ctx.semantic().scoping();
let symbols = ctx.scoping();

// get the AST node + symbol id of the declaration of the identifier
let reference = symbols.get_reference(ident.reference_id());
let Some(referenced_symbol_id) = reference.symbol_id() else {
return;
};
let declaration_id = symbols.get_symbol_declaration(referenced_symbol_id);
let Some(declaration) = ctx.semantic().nodes().parent_node(declaration_id) else {
let Some(declaration) = ctx.nodes().parent_node(declaration_id) else {
return;
};

check_reduce_usage(declaration, referenced_symbol_id, spread.span, ctx);
check_loop_usage(
declaration,
ctx.semantic().nodes().get_node(declaration_id),
ctx.nodes().get_node(declaration_id),
referenced_symbol_id,
node.id(),
spread.span,
Expand Down
Loading
Loading