Skip to content

Commit

Permalink
fix(semantic): export default foo should have `ExportLocalName::Def…
Browse files Browse the repository at this point in the history
…ault(NameSpan)` entry (#3823)
  • Loading branch information
Boshen committed Jun 22, 2024
1 parent 7302429 commit 99a40ce
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 102 deletions.
108 changes: 50 additions & 58 deletions crates/oxc_linter/src/rules/nextjs/no_page_custom_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ impl Rule for NoPageCustomFont {
let AstKind::JSXOpeningElement(element) = node.kind() else {
return;
};
if matches!(&element.name, JSXElementName::Identifier(ident) if ident.name != "link") {
let JSXElementName::Identifier(ident) = &element.name else { return };

if ident.name != "link" {
return;
}

Expand All @@ -58,63 +60,12 @@ impl Rule for NoPageCustomFont {
return;
}

let mut is_inside_export_default = false;
for parent_node in ctx.nodes().iter_parents(node.id()) {
// export default function/class
let kind = parent_node.kind();
if matches!(kind, AstKind::ExportDefaultDeclaration(_)) {
is_inside_export_default = true;
break;
}

// function variable() {}; export default variable;
let id = match kind {
AstKind::ArrowFunctionExpression(_) => None,
AstKind::Function(Function { id, .. }) | AstKind::Class(Class { id, .. }) => {
id.clone()
}
_ => continue,
};

let name = id.map_or_else(
|| {
let parent_parent_kind = ctx.nodes().parent_kind(parent_node.id())?;

let AstKind::VariableDeclarator(declarator) = parent_parent_kind else {
return None;
};
declarator.id.get_identifier().map(|id| id.to_string())
},
|id| Some(id.name.to_string()),
);
let Some(name) = name else {
continue;
};
if let Some(symbol_id) = ctx.scopes().get_root_binding(&name) {
if ctx.symbols().get_flag(symbol_id).is_export() {
let is_export_default =
ctx.symbols().get_resolved_references(symbol_id).any(|reference| {
reference.is_read()
&& matches!(
ctx.nodes().parent_kind(reference.node_id()),
Some(AstKind::ExportDefaultDeclaration(_))
)
});

if is_export_default {
is_inside_export_default = true;
break;
}
}
}
}

let in_document = ctx.file_path().file_name().map_or(false, |file_name| {
file_name.to_str().map_or(false, |file_name| file_name.starts_with("_document."))
});
let span = ctx.nodes().parent_kind(node.id()).unwrap().span();
let diagnostic = if in_document {
if is_inside_export_default {
if is_inside_export_default(node, ctx) {
return;
}
link_outside_of_head(span)
Expand All @@ -125,6 +76,47 @@ impl Rule for NoPageCustomFont {
}
}

fn is_inside_export_default(node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool {
let mut is_inside_export_default = false;
for parent_node in ctx.nodes().iter_parents(node.id()) {
// export default function/class
let kind = parent_node.kind();
if matches!(kind, AstKind::ExportDefaultDeclaration(_)) {
is_inside_export_default = true;
break;
}

// function variable() {}; export default variable;
let id = match kind {
AstKind::ArrowFunctionExpression(_) => None,
AstKind::Function(Function { id, .. }) | AstKind::Class(Class { id, .. }) => id.clone(),
_ => continue,
};

let name = id.map_or_else(
|| {
let parent_parent_kind = ctx.nodes().parent_kind(parent_node.id())?;

let AstKind::VariableDeclarator(declarator) = parent_parent_kind else {
return None;
};
declarator.id.get_identifier().map(|id| id.to_string())
},
|id| Some(id.name.to_string()),
);
let Some(name) = name else {
continue;
};
if ctx.module_record().local_export_entries.iter().any(|e| {
e.local_name.is_default()
&& e.local_name.name().is_some_and(|n| n.as_str() == name.as_str())
}) {
is_inside_export_default = true;
}
}
is_inside_export_default
}

#[test]
fn test() {
use std::path::PathBuf;
Expand Down Expand Up @@ -206,7 +198,7 @@ fn test() {
</Html>
)
}
export default CustomDocument;
"#,
None,
Expand All @@ -230,7 +222,7 @@ fn test() {
);
}
}
export default MyDocument;"#,
None,
None,
Expand Down Expand Up @@ -297,8 +289,8 @@ fn test() {
(
r#"
import Head from 'next/head'
function Links() {
return (
<>
Expand All @@ -313,7 +305,7 @@ fn test() {
</>
)
}
export default function IndexPage() {
return (
<div>
Expand Down
30 changes: 6 additions & 24 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ use oxc_cfg::{
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{CompactStr, SourceType, Span};
use oxc_syntax::{
identifier::is_identifier_name,
module_record::{ExportImportName, ExportLocalName, ModuleRecord},
module_record::{ExportImportName, ModuleRecord},
operator::AssignmentOperator,
};

Expand Down Expand Up @@ -399,23 +398,10 @@ impl<'a> SemanticBuilder<'a> {
});

self.module_record.local_export_entries.iter().for_each(|entry| {
match &entry.local_name {
ExportLocalName::Name(name_span) => {
if let Some(symbol_id) = self.scope.get_root_binding(name_span.name()) {
self.symbols.union_flag(symbol_id, SymbolFlags::Export);
}
}
ExportLocalName::Default(_) => {
// export default identifier
// ^^^^^^^^^^
let identifier = entry.span.source_text(self.source_text);
if is_identifier_name(identifier) {
if let Some(symbol_id) = self.scope.get_root_binding(identifier) {
self.symbols.union_flag(symbol_id, SymbolFlags::Export);
}
}
if let Some(name) = entry.local_name.name() {
if let Some(symbol_id) = self.scope.get_root_binding(name.as_str()) {
self.symbols.union_flag(symbol_id, SymbolFlags::Export);
}
ExportLocalName::Null => {}
}
});
}
Expand Down Expand Up @@ -1726,7 +1712,7 @@ impl<'a> SemanticBuilder<'a> {
AstKind::Class(class) => {
self.current_node_flags |= NodeFlags::Class;
class.bind(self);
self.remove_export_flag();
self.current_symbol_flags -= SymbolFlags::Export;
self.make_all_namespaces_valuelike();
}
AstKind::ClassBody(body) => {
Expand All @@ -1748,7 +1734,7 @@ impl<'a> SemanticBuilder<'a> {
}
AstKind::FormalParameters(_) => {
self.current_node_flags |= NodeFlags::Parameter;
self.remove_export_flag();
self.current_symbol_flags -= SymbolFlags::Export;
}
AstKind::FormalParameter(param) => {
param.bind(self);
Expand Down Expand Up @@ -1878,10 +1864,6 @@ impl<'a> SemanticBuilder<'a> {
}
}

fn remove_export_flag(&mut self) {
self.current_symbol_flags -= SymbolFlags::Export;
}

fn add_current_node_id_to_current_scope(&mut self) {
self.scope.add_node_id(self.current_scope_id, self.current_node_id);
}
Expand Down
30 changes: 18 additions & 12 deletions crates/oxc_semantic/src/module_record/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,22 +243,28 @@ impl ModuleRecordBuilder {
return;
}
let exported_name = &decl.exported;
self.add_default_export(exported_name.span());
let exported_name_span = decl.exported.span();
self.add_default_export(exported_name_span);

let id = match &decl.declaration {
match_expression!(ExportDefaultDeclarationKind) => None,
ExportDefaultDeclarationKind::FunctionDeclaration(func) => func.id.as_ref(),
ExportDefaultDeclarationKind::ClassDeclaration(class) => class.id.as_ref(),
ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) => return,
let local_name = match &decl.declaration {
ExportDefaultDeclarationKind::Identifier(ident) => {
ExportLocalName::Default(NameSpan::new(ident.name.to_compact_str(), 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)),
)
}
ExportDefaultDeclarationKind::ClassDeclaration(class) => class.id.as_ref().map_or_else(
|| ExportLocalName::Null,
|id| ExportLocalName::Name(NameSpan::new(id.name.to_compact_str(), id.span)),
),
_ => ExportLocalName::Null,
};
let export_entry = ExportEntry {
export_name: ExportExportName::Default(exported_name.span()),
local_name: id.as_ref().map_or_else(
|| ExportLocalName::Default(exported_name.span()),
|ident| {
ExportLocalName::Name(NameSpan::new(ident.name.to_compact_str(), ident.span))
},
),
local_name,
span: decl.declaration.span(),
..ExportEntry::default()
};
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_semantic/src/module_record/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ mod module_record_tests {
let module_record = build("export default function() {}");
let export_entry = ExportEntry {
export_name: ExportExportName::Default(Span::new(7, 14)),
local_name: ExportLocalName::Default(Span::new(7, 14)),
local_name: ExportLocalName::Null,
span: Span::new(15, 28),
..ExportEntry::default()
};
Expand All @@ -231,7 +231,7 @@ mod module_record_tests {
let module_record = build("export default 42");
let export_entry = ExportEntry {
export_name: ExportExportName::Default(Span::new(7, 14)),
local_name: ExportLocalName::Default(Span::new(7, 14)),
local_name: ExportLocalName::Null,
span: Span::new(15, 17),
..ExportEntry::default()
};
Expand Down
13 changes: 7 additions & 6 deletions crates/oxc_syntax/src/module_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ impl ExportExportName {
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub enum ExportLocalName {
Name(NameSpan),
Default(Span),
/// `export default name_span`
Default(NameSpan),
#[default]
Null,
}
Expand All @@ -262,8 +263,8 @@ impl ExportLocalName {

pub const fn name(&self) -> Option<&CompactStr> {
match self {
Self::Name(name) => Some(name.name()),
Self::Default(_) | Self::Null => None,
Self::Name(name) | Self::Default(name) => Some(name.name()),
Self::Null => None,
}
}
}
Expand Down Expand Up @@ -332,11 +333,11 @@ mod test {
fn export_local_name() {
let name = NameSpan::new("name".into(), Span::new(0, 0));
assert!(!ExportLocalName::Name(name.clone()).is_default());
assert!(ExportLocalName::Default(Span::new(0, 0)).is_default());
assert!(ExportLocalName::Default(name.clone()).is_default());
assert!(!ExportLocalName::Null.is_default());

assert!(!ExportLocalName::Name(name).is_null());
assert!(!ExportLocalName::Default(Span::new(0, 0)).is_null());
assert!(!ExportLocalName::Name(name.clone()).is_null());
assert!(!ExportLocalName::Default(name.clone()).is_null());
assert!(ExportLocalName::Null.is_null());
}
}

0 comments on commit 99a40ce

Please sign in to comment.