Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(semantic): incorrect reference in parameters #2644

Closed

Conversation

Dunqing
Copy link
Member

@Dunqing Dunqing commented Mar 8, 2024

close: #2499

We need to collect parameter bindings. If an IdentifierReference is used on a parameter, we need to check if the current parameter binding is already defined, if not then it can only access the parent scope's bindings.

Let's look at an example.

const foo = 0;
function a(b = foo, c = b) {
   const foo = 1;
   const c = 2;
}  

The parameter has a reference to foo, but there is no foo in the parameter binding, so it must access the parent scope. Let's look at c = b, which has a reference to b, and the first parameter already has a b binding defined, so in the current scope we can access b.

I apologize for adding two more properties to the SemanticBuilder, the current implementation can't avoid adding them. Please let me know if you have a better solution, I'm interested in a better solution!

Copy link
Member Author

Dunqing commented Mar 8, 2024

This stack of pull requests is managed by Graphite. Learn more about stacking.

Join @Dunqing and the rest of your teammates on Graphite Graphite

@github-actions github-actions bot added the A-semantic Area - Semantic label Mar 8, 2024
Copy link

codspeed-hq bot commented Mar 8, 2024

CodSpeed Performance Report

Merging #2644 will not alter performance

Comparing 03-08-fix_semantic_Incorrect_reference_in_parameters (7550838) with main (c72675e)

Summary

✅ 29 untouched benchmarks

@Dunqing Dunqing requested a review from Boshen March 8, 2024 13:46
@Dunqing
Copy link
Member Author

Dunqing commented Mar 8, 2024

@hyf0 cc

@hyf0
Copy link
Contributor

hyf0 commented Mar 8, 2024

Would it be easier to create a facade scope for the params part?

@Boshen
Copy link
Member

Boshen commented Mar 8, 2024

My questions will always be: how does TypeScript do it?

Another questions, can we do everything inside FormalParameters instead of

impl<'a> Binder for BindingRestElement<'a> {
// Binds the FormalParameters's rest of a function or method.
fn bind(&self, builder: &mut SemanticBuilder) {
let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap();
let AstKind::FormalParameters(parameters) = parent_kind else {
return;
};
if parameters.kind.is_signature() {
return;
}
let includes = SymbolFlags::FunctionScopedVariable;
let excludes =
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes;
self.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
});
}
}
impl<'a> Binder for FormalParameter<'a> {
// Binds the FormalParameter of a function or method.
fn bind(&self, builder: &mut SemanticBuilder) {
let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap();
let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() };
if parameters.kind.is_signature() {
return;
}
let includes = SymbolFlags::FunctionScopedVariable;
let is_not_allowed_duplicate_parameters = matches!(
parameters.kind,
// ArrowFormalParameters: UniqueFormalParameters
FormalParameterKind::ArrowFormalParameters |
// UniqueFormalParameters : FormalParameters
// * It is a Syntax Error if BoundNames of FormalParameters contains any duplicate elements.
FormalParameterKind::UniqueFormalParameters
) ||
// Multiple occurrences of the same BindingIdentifier in a FormalParameterList is only allowed for functions which have simple parameter lists and which are not defined in strict mode code.
builder.strict_mode() ||
// FormalParameters : FormalParameterList
// * It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false and BoundNames of FormalParameterList contains any duplicate elements.
!parameters.is_simple_parameter_list();
let excludes = if is_not_allowed_duplicate_parameters {
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes
} else {
SymbolFlags::FunctionScopedVariableExcludes
};
self.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
});
}
}

@Dunqing
Copy link
Member Author

Dunqing commented Mar 8, 2024

My questions will always be: how does TypeScript do it?

I've taken a cursory look at typescript, but I don't see a specific way to handle it yet

Another questions, can we do everything inside FormalParameters instead of

impl<'a> Binder for BindingRestElement<'a> {
// Binds the FormalParameters's rest of a function or method.
fn bind(&self, builder: &mut SemanticBuilder) {
let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap();
let AstKind::FormalParameters(parameters) = parent_kind else {
return;
};
if parameters.kind.is_signature() {
return;
}
let includes = SymbolFlags::FunctionScopedVariable;
let excludes =
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes;
self.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
});
}
}
impl<'a> Binder for FormalParameter<'a> {
// Binds the FormalParameter of a function or method.
fn bind(&self, builder: &mut SemanticBuilder) {
let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap();
let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() };
if parameters.kind.is_signature() {
return;
}
let includes = SymbolFlags::FunctionScopedVariable;
let is_not_allowed_duplicate_parameters = matches!(
parameters.kind,
// ArrowFormalParameters: UniqueFormalParameters
FormalParameterKind::ArrowFormalParameters |
// UniqueFormalParameters : FormalParameters
// * It is a Syntax Error if BoundNames of FormalParameters contains any duplicate elements.
FormalParameterKind::UniqueFormalParameters
) ||
// Multiple occurrences of the same BindingIdentifier in a FormalParameterList is only allowed for functions which have simple parameter lists and which are not defined in strict mode code.
builder.strict_mode() ||
// FormalParameters : FormalParameterList
// * It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false and BoundNames of FormalParameterList contains any duplicate elements.
!parameters.is_simple_parameter_list();
let excludes = if is_not_allowed_duplicate_parameters {
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes
} else {
SymbolFlags::FunctionScopedVariableExcludes
};
self.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
});
}
}

CatchClause no FormalParameters

fn visit_catch_clause(&mut self, clause: &CatchClause<'a>) {
let kind = AstKind::CatchClause(self.alloc(clause));
self.enter_scope(ScopeFlags::empty());
self.enter_node(kind);
if let Some(param) = &clause.param {
self.visit_binding_pattern(param);
}
self.visit_statements(&clause.body.body);
self.leave_node(kind);
self.leave_scope();
}

@Boshen
Copy link
Member

Boshen commented Mar 8, 2024

I think catch clause is another special case ...

@Dunqing
Copy link
Member Author

Dunqing commented Mar 9, 2024

Another questions, can we do everything inside FormalParameters instead of

impl<'a> Binder for BindingRestElement<'a> {
// Binds the FormalParameters's rest of a function or method.
fn bind(&self, builder: &mut SemanticBuilder) {
let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap();
let AstKind::FormalParameters(parameters) = parent_kind else {
return;
};
if parameters.kind.is_signature() {
return;
}
let includes = SymbolFlags::FunctionScopedVariable;
let excludes =
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes;
self.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
});
}
}
impl<'a> Binder for FormalParameter<'a> {
// Binds the FormalParameter of a function or method.
fn bind(&self, builder: &mut SemanticBuilder) {
let parent_kind = builder.nodes.parent_kind(builder.current_node_id).unwrap();
let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() };
if parameters.kind.is_signature() {
return;
}
let includes = SymbolFlags::FunctionScopedVariable;
let is_not_allowed_duplicate_parameters = matches!(
parameters.kind,
// ArrowFormalParameters: UniqueFormalParameters
FormalParameterKind::ArrowFormalParameters |
// UniqueFormalParameters : FormalParameters
// * It is a Syntax Error if BoundNames of FormalParameters contains any duplicate elements.
FormalParameterKind::UniqueFormalParameters
) ||
// Multiple occurrences of the same BindingIdentifier in a FormalParameterList is only allowed for functions which have simple parameter lists and which are not defined in strict mode code.
builder.strict_mode() ||
// FormalParameters : FormalParameterList
// * It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false and BoundNames of FormalParameterList contains any duplicate elements.
!parameters.is_simple_parameter_list();
let excludes = if is_not_allowed_duplicate_parameters {
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes
} else {
SymbolFlags::FunctionScopedVariableExcludes
};
self.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
});
}
}

Initially, we did everything in FormalParameters, but that would result in the node query by node id not being specific enough
#2114

@Boshen
Copy link
Member

Boshen commented Mar 9, 2024

Ahh ... I'll take a look at this next week :-(

@Boshen Boshen closed this in #3046 Apr 21, 2024
Boshen added a commit that referenced this pull request Apr 21, 2024
…izers (#3046)

close: #2644
This fixes function parameters. I think we need an extra AST node to fix catch parameters, which will be the next PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-semantic Area - Semantic
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Wrong scope analysis for catch block scope
3 participants