Skip to content

Commit

Permalink
fix(semantic): incorrect reference flag for TSTypeParameter
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing committed Feb 18, 2024
1 parent 90f9266 commit 4282891
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 7 deletions.
13 changes: 10 additions & 3 deletions crates/oxc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,12 @@ pub trait Visit<'a>: Sized {
// E.g., `let c = class A { foo() { console.log(A) } }`
self.enter_scope(ScopeFlags::empty());
}

self.enter_node(kind);

if let Some(id) = &class.id {
self.visit_binding_identifier(id);
}
self.enter_scope(ScopeFlags::TypeBlock);
if let Some(parameters) = &class.type_parameters {
self.visit_ts_type_parameter_declaration(parameters);
}
Expand All @@ -455,6 +455,7 @@ pub trait Visit<'a>: Sized {
}
self.visit_class_body(&class.body);
self.leave_node(kind);
self.leave_scope();
if is_class_expr {
self.leave_scope();
}
Expand Down Expand Up @@ -1541,24 +1542,28 @@ pub trait Visit<'a>: Sized {
let kind = AstKind::TSTypeAliasDeclaration(self.alloc(decl));
self.enter_node(kind);
self.visit_binding_identifier(&decl.id);
self.enter_scope(ScopeFlags::TypeBlock);
if let Some(parameters) = &decl.type_parameters {
self.visit_ts_type_parameter_declaration(parameters);
}
self.visit_ts_type(&decl.type_annotation);
self.leave_node(kind);
self.leave_scope();
}

fn visit_ts_interface_declaration(&mut self, decl: &TSInterfaceDeclaration<'a>) {
let kind = AstKind::TSInterfaceDeclaration(self.alloc(decl));
self.enter_node(kind);
self.visit_binding_identifier(&decl.id);
self.enter_scope(ScopeFlags::TypeBlock);
if let Some(parameters) = &decl.type_parameters {
self.visit_ts_type_parameter_declaration(parameters);
}
for signature in &decl.body.body {
self.visit_ts_signature(signature);
}
self.leave_node(kind);
self.leave_scope();
}

fn visit_ts_as_expression(&mut self, expr: &TSAsExpression<'a>) {
Expand Down Expand Up @@ -1690,7 +1695,6 @@ pub trait Visit<'a>: Sized {

fn visit_ts_type_parameter(&mut self, ty: &TSTypeParameter<'a>) {
let kind = AstKind::TSTypeParameter(self.alloc(ty));
self.enter_scope(ScopeFlags::empty());
self.enter_node(kind);
if let Some(constraint) = &ty.constraint {
self.visit_ts_type(constraint);
Expand All @@ -1700,7 +1704,6 @@ pub trait Visit<'a>: Sized {
self.visit_ts_type(default);
}
self.leave_node(kind);
self.leave_scope();
}

fn visit_ts_type_parameter_instantiation(&mut self, ty: &TSTypeParameterInstantiation<'a>) {
Expand Down Expand Up @@ -1820,17 +1823,20 @@ pub trait Visit<'a>: Sized {
&mut self,
signature: &TSConstructSignatureDeclaration<'a>,
) {
self.enter_scope(ScopeFlags::TypeBlock);
self.visit_formal_parameters(&signature.params);
if let Some(parameters) = &signature.type_parameters {
self.visit_ts_type_parameter_declaration(parameters);
}
if let Some(annotation) = &signature.return_type {
self.visit_ts_type_annotation(annotation);
}
self.leave_scope();
}

fn visit_ts_method_signature(&mut self, signature: &TSMethodSignature<'a>) {
let kind = AstKind::TSMethodSignature(self.alloc(signature));
self.enter_scope(ScopeFlags::TypeBlock);
self.enter_node(kind);
self.visit_formal_parameters(&signature.params);
if let Some(parameters) = &signature.type_parameters {
Expand All @@ -1840,6 +1846,7 @@ pub trait Visit<'a>: Sized {
self.visit_ts_type_annotation(annotation);
}
self.leave_node(kind);
self.leave_scope();
}

fn visit_ts_index_signature_name(&mut self, name: &TSIndexSignatureName<'a>) {
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1553,7 +1553,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
// E.g., `let c = class A { foo() { console.log(A) } }`
self.enter_scope(ScopeFlags::empty());
}

self.enter_node(kind);

/* cfg */
Expand All @@ -1564,6 +1563,8 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
if let Some(id) = &class.id {
self.visit_binding_identifier(id);
}
self.enter_scope(ScopeFlags::TypeBlock);

if let Some(parameters) = &class.type_parameters {
self.visit_ts_type_parameter_declaration(parameters);
}
Expand All @@ -1586,6 +1587,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
/* cfg */

self.leave_node(kind);
self.leave_scope();
if is_class_expr {
self.leave_scope();
}
Expand Down
61 changes: 61 additions & 0 deletions crates/oxc_semantic/tests/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,64 @@ fn test_types_simple() {
.has_number_of_references(1)
.test();
}

#[test]
fn test_type_parameter() {
let tester = SemanticTester::ts(
"
type A<AB> = AB
type B<AB> = AB
interface C<CC> {
C: CC;
}
class D<DD> {
a: DD;
method(b: DD) {
}
}
function F<FF>() {
return '' as FF
}
",
);

tester
.has_symbol("AB")
.contains_flags(SymbolFlags::TypeParameter)
.has_number_of_references(1)
.test();

tester
.has_symbol("CC")
.contains_flags(SymbolFlags::TypeParameter)
.has_number_of_references(1)
.test();

tester
.has_symbol("DD")
.contains_flags(SymbolFlags::TypeParameter)
.has_number_of_references(2)
.test();

tester
.has_symbol("FF")
.contains_flags(SymbolFlags::TypeParameter)
.has_number_of_references(1)
.test();
}

#[test]
fn test_type_parameter_name_same_with_other_symbols() {
SemanticTester::ts(
"
interface CC {
}
class A<CC> {
a: CC;
}
",
)
.has_symbol("CC")
.test();
}
9 changes: 9 additions & 0 deletions crates/oxc_semantic/tests/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ impl<'a> SemanticTester<'a> {
SymbolTester::new_unique(self, self.build(), name)
}

/// Find first symbol by name in the source code.
///
/// ## Fails
/// 1. No symbol with the given name exists,
#[allow(dead_code)]
pub fn has_symbol(&self, name: &str) -> SymbolTester {
SymbolTester::new_first_binding(self, self.build(), name)
}

fn wrap_diagnostics(&self, diagnostics: Vec<Error>) -> Vec<Error> {
let name = "test".to_owned()
+ match (self.source_type.is_javascript(), self.source_type.is_jsx()) {
Expand Down
23 changes: 22 additions & 1 deletion crates/oxc_semantic/tests/util/symbol_tester.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::rc::Rc;

use oxc_diagnostics::{miette::miette, Error};
use oxc_semantic::{Reference, ScopeFlags, Semantic, SymbolFlags, SymbolId};
use oxc_semantic::{Reference, ScopeFlags, ScopeId, Semantic, SymbolFlags, SymbolId};
use oxc_span::Atom;

use super::{Expect, SemanticTester};
Expand Down Expand Up @@ -56,6 +56,27 @@ impl<'a> SymbolTester<'a> {
}
}

pub(super) fn new_first_binding(
parent: &'a SemanticTester,
semantic: Semantic<'a>,
target: &str,
) -> Self {
let symbols_with_target_name: Option<(ScopeId, SymbolId, Atom)> =
semantic.scopes().iter_bindings().find(|(_, _, name)| name == &target);

let data = match symbols_with_target_name {
Some((_, symbol_id, _)) => Ok(symbol_id),
None => Err(miette!("Could not find declaration for {target}")),
};

SymbolTester {
parent,
semantic: Rc::new(semantic),
target_symbol_name: target.to_string(),
test_result: data,
}
}

/// Checks if the resolved symbol contains all flags in `flags`, using [`SymbolFlags::contains()`]
pub fn contains_flags(mut self, flags: SymbolFlags) -> Self {
self.test_result = match self.test_result {
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_syntax/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ bitflags! {
const Constructor = 1 << 6;
const GetAccessor = 1 << 7;
const SetAccessor = 1 << 8;
const TypeBlock = 1 << 9;
const Var = Self::Top.bits() | Self::Function.bits() | Self::ClassStaticBlock.bits() | Self::TsModuleBlock.bits();
const Modifiers = Self::Constructor.bits() | Self::GetAccessor.bits() | Self::SetAccessor.bits();
}
Expand Down
15 changes: 13 additions & 2 deletions tasks/coverage/parser_typescript.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
parser_typescript Summary:
AST Parsed : 5239/5243 (99.92%)
Positive Passed: 5232/5243 (99.79%)
Negative Passed: 1040/4879 (21.32%)
Negative Passed: 1041/4879 (21.34%)
Expect Syntax Error: "compiler/ClassDeclaration10.ts"
Expect Syntax Error: "compiler/ClassDeclaration11.ts"
Expect Syntax Error: "compiler/ClassDeclaration13.ts"
Expand Down Expand Up @@ -3521,7 +3521,6 @@ Expect Syntax Error: "conformance/types/literal/templateLiteralTypes3.ts"
Expect Syntax Error: "conformance/types/literal/templateLiteralTypes4.ts"
Expect Syntax Error: "conformance/types/literal/templateLiteralTypesPatterns.ts"
Expect Syntax Error: "conformance/types/literal/templateLiteralTypesPatternsPrefixSuffixAssignability.ts"
Expect Syntax Error: "conformance/types/localTypes/localTypes4.ts"
Expect Syntax Error: "conformance/types/mapped/mappedTypeAsClauseRelationships.ts"
Expect Syntax Error: "conformance/types/mapped/mappedTypeAsClauses.ts"
Expect Syntax Error: "conformance/types/mapped/mappedTypeConstraints2.ts"
Expand Down Expand Up @@ -19286,6 +19285,18 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
2 │ const a = import<string, number>
╰────

× Identifier `T` has already been declared
╭─[conformance/types/localTypes/localTypes4.ts:20:16]
19 │ // Type parameters and top-level local types are in same declaration space
20 │ function f<T>() {
· ┬
· ╰── It can not be redeclared here
21 │ interface T { }
· ┬
· ╰── `T` has already been declared here
22 │ return undefined;
╰────

× Expected `}` but found `Identifier`
╭─[conformance/types/mapped/mappedTypeProperties.ts:10:5]
9 │ [placeType in PlaceType]: void;
Expand Down

0 comments on commit 4282891

Please sign in to comment.