Skip to content

Commit 3260002

Browse files
authored
Merge pull request #22782 from hvitved/unified/static-name-binding-shadowing
Unified: Improve static name binding involving shadowing
2 parents b1200b5 + b1da660 commit 3260002

7 files changed

Lines changed: 309 additions & 55 deletions

File tree

‎unified/extractor/ast_types.yml‎

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,20 @@ supertypes:
8181
body?: block
8282
# A member is anything that can appear in the body of a class-like declaration
8383
member:
84-
- constructor_declaration
85-
- destructor_declaration
86-
- function_declaration
87-
- variable_declaration
88-
- accessor_declaration
89-
- initializer_declaration
90-
- class_like_declaration
91-
- type_alias_declaration
92-
- associated_type_declaration
93-
- unsupported_node
94-
- unhandled_node
84+
subtypes:
85+
- constructor_declaration
86+
- destructor_declaration
87+
- function_declaration
88+
- variable_declaration
89+
- accessor_declaration
90+
- initializer_declaration
91+
- class_like_declaration
92+
- type_alias_declaration
93+
- associated_type_declaration
94+
- unsupported_node
95+
- unhandled_node
96+
fields:
97+
name_node: identifier
9598
type_constraint:
9699
- equality_type_constraint
97100
- bound_type_constraint

‎unified/ql/lib/codeql/unified/internal/Ast.qll‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ module Unified {
113113
final F::Modifier getAModifier() { result = this.getModifier(_) }
114114

115115
/** Gets the node corresponding to the field `name_node`. */
116-
final F::Identifier getNameNode() { unified_accessor_declaration_def(this, _, result) }
116+
final override F::Identifier getNameNode() { unified_accessor_declaration_def(this, _, result) }
117117

118118
/** Gets the node corresponding to the field `parameter`. */
119119
final override F::Parameter getParameter(int i) {
@@ -200,7 +200,9 @@ module Unified {
200200
final F::Modifier getAModifier() { result = this.getModifier(_) }
201201

202202
/** Gets the node corresponding to the field `name_node`. */
203-
final F::Identifier getNameNode() { unified_associated_type_declaration_def(this, result) }
203+
final override F::Identifier getNameNode() {
204+
unified_associated_type_declaration_def(this, result)
205+
}
204206

205207
/** Gets a field or child node of this node. */
206208
final override F::AstNode getAFieldOrChild() {
@@ -427,7 +429,9 @@ module Unified {
427429
final F::Modifier getAModifier() { result = this.getModifier(_) }
428430

429431
/** Gets the node corresponding to the field `name_node`. */
430-
final F::Identifier getNameNode() { unified_class_like_declaration_name_node(this, result) }
432+
final override F::Identifier getNameNode() {
433+
unified_class_like_declaration_name_node(this, result)
434+
}
431435

432436
/** Gets the node corresponding to the field `type_constraint`. */
433437
final F::TypeConstraint getTypeConstraint(int i) {
@@ -501,7 +505,9 @@ module Unified {
501505
final F::Modifier getAModifier() { result = this.getModifier(_) }
502506

503507
/** Gets the node corresponding to the field `name_node`. */
504-
final F::Identifier getNameNode() { unified_constructor_declaration_name_node(this, result) }
508+
final override F::Identifier getNameNode() {
509+
unified_constructor_declaration_name_node(this, result)
510+
}
505511

506512
/** Gets the node corresponding to the field `parameter`. */
507513
final override F::Parameter getParameter(int i) {
@@ -704,7 +710,7 @@ module Unified {
704710
final F::Modifier getAModifier() { result = this.getModifier(_) }
705711

706712
/** Gets the node corresponding to the field `name_node`. */
707-
final F::Identifier getNameNode() { unified_function_declaration_def(this, result) }
713+
final override F::Identifier getNameNode() { unified_function_declaration_def(this, result) }
708714

709715
/** Gets the node corresponding to the field `parameter`. */
710716
final override F::Parameter getParameter(int i) {
@@ -1007,7 +1013,10 @@ module Unified {
10071013
final override F::AstNode getAFieldOrChild() { unified_map_literal_element(this, _, result) }
10081014
}
10091015

1010-
class Member extends @unified_member, F::AstNode { }
1016+
class Member extends @unified_member, F::AstNode {
1017+
/** Gets the node corresponding to the field `name_node`. */
1018+
F::Identifier getNameNode() { none() }
1019+
}
10111020

10121021
/** A class representing `member_access_expr` nodes. */
10131022
class MemberAccessExpr extends @unified_member_access_expr, F::Expr {
@@ -1369,7 +1378,9 @@ module Unified {
13691378
final F::Modifier getAModifier() { result = this.getModifier(_) }
13701379

13711380
/** Gets the node corresponding to the field `name_node`. */
1372-
final F::Identifier getNameNode() { unified_type_alias_declaration_def(this, result, _) }
1381+
final override F::Identifier getNameNode() {
1382+
unified_type_alias_declaration_def(this, result, _)
1383+
}
13731384

13741385
/** Gets the node corresponding to the field `type`. */
13751386
final F::Expr getType() { unified_type_alias_declaration_def(this, _, result) }

‎unified/ql/lib/codeql/unified/internal/FacadeAst.qll‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ module Unified {
193193

194194
class VariableDeclaration extends G::VariableDeclaration {
195195
/** Gets the name node of this variable declaration, if any. */
196-
Identifier getNameNode() { result = this.getPattern() }
196+
override Identifier getNameNode() { result = this.getPattern() }
197197

198198
/** Gets the name of the variable being declared, if any. */
199199
string getName() { result = this.getNameNode().getValue() }

‎unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
private import unified
22
private import codeql.util.Unit
3+
private import codeql.util.Option
4+
private import StaticNameBinding
35

46
private module Plugins {
57
private import codeql.unified.internal.NameBindingPluginSwift
@@ -43,6 +45,23 @@ class NameBindingPlugin extends Unit {
4345
bindingset[cls, member]
4446
predicate isInheritableMember(ClassLikeDeclaration cls, Member member) { none() }
4547

48+
/**
49+
* Holds if `member` is considered invalid within the namespace `n`.
50+
*/
51+
bindingset[n, member]
52+
predicate isInvalidMember(NamespaceNode n, Member member) { none() }
53+
54+
/**
55+
* Gets the key used to determine if `m` is shadowed by another declaration
56+
* with the same name and key.
57+
*
58+
* Nodes without shadowing keys will not shadow inherited declarations.
59+
*
60+
* This means that shadowing can be completely disabled by not implementing this
61+
* predicate, and completely enabled by assigning the same key to all members.
62+
*/
63+
string getShadowingKey(Member m) { none() }
64+
4665
/** Gets the name of the implicit receiver parameter in `callable`, if it has one. */
4766
string getImplicitReceiverParameterName(Callable callable) { none() }
4867

@@ -89,6 +108,28 @@ predicate isInheritableMember(Member member) {
89108
)
90109
}
91110

111+
bindingset[n, member]
112+
predicate isInvalidMember(NamespaceNode n, Member member) {
113+
any(NameBindingPlugin p).isInvalidMember(n, member)
114+
}
115+
116+
private string getShadowingKey0(NameBindingNode n) {
117+
result = any(NameBindingPlugin p).getShadowingKey(any(Member m | n.isMember(m)))
118+
}
119+
120+
private class ShadowingKey extends string {
121+
ShadowingKey() { this = getShadowingKey0(_) }
122+
}
123+
124+
class ShadowingKeyOpt = Option<ShadowingKey>::Option;
125+
126+
ShadowingKeyOpt getShadowingKey(NameBindingNode n) {
127+
result.asSome() = getShadowingKey0(n)
128+
or
129+
not exists(getShadowingKey0(n)) and
130+
result.isNone()
131+
}
132+
92133
/**
93134
* A representative for a module scope.
94135
*

‎unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll‎

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,21 @@
33
*/
44

55
private import unified
6+
private import codeql.unified.internal.StaticNameBinding
67
private import codeql.unified.internal.NameBindingPlugin
78

9+
private class GeneratedConstructor extends ConstructorDeclaration {
10+
GeneratedConstructor() { this.hasModifier("generated") }
11+
}
12+
13+
private class ConvenienceConstructor extends ConstructorDeclaration {
14+
ConvenienceConstructor() { this.hasModifier("convenience") }
15+
}
16+
17+
private class DesignatedConstructor extends ConstructorDeclaration {
18+
DesignatedConstructor() { not this instanceof ConvenienceConstructor }
19+
}
20+
821
class NameBindingPluginSwift extends NameBindingPlugin {
922
bindingset[e]
1023
override predicate isNonPattern(Expr e) { isUnboundPattern(e.(Identifier)) }
@@ -36,7 +49,48 @@ class NameBindingPluginSwift extends NameBindingPlugin {
3649
bindingset[cls, member]
3750
override predicate isInheritableMember(ClassLikeDeclaration cls, Member member) {
3851
exists(cls) and
39-
not member.hasModifier("private")
52+
not member.hasModifier("private") and
53+
not (cls.hasModifier("protocol") and member instanceof ConstructorDeclaration)
54+
}
55+
56+
bindingset[n, member]
57+
override predicate isInvalidMember(NamespaceNode n, Member member) {
58+
exists(NamespaceNode parent |
59+
parent = n.getAnInheritanceParent() and
60+
not parent
61+
.isInstanceOrStaticMemberNamespace(any(ClassLikeDeclaration p | p.hasModifier("protocol")))
62+
|
63+
// Remove generated constructors when there are inherited constructors available
64+
// Note: If the base class only has private constructors, this class must have an
65+
// explicit constructor, in which case there is no generated constructor to begin
66+
// with
67+
member instanceof GeneratedConstructor and
68+
n.getOwnMember(_).isMember(member) and
69+
parent.getMemberFull(_, _).isMember(any(ConstructorDeclaration inherited))
70+
or
71+
// Remove inherited designated constructors (generated or not) when there are
72+
// explicit designated constructors available
73+
// Note: We always inherit convenience constructors, even though it may not actually
74+
// be the case in Swift; this should be OK, since there can then not exist any calls
75+
// that target those constructors
76+
parent.getMemberFull(_, _).isMember(member.(DesignatedConstructor)) and
77+
exists(DesignatedConstructor designated |
78+
n.getOwnMember(_).isMember(designated) and
79+
not designated instanceof GeneratedConstructor
80+
)
81+
)
82+
}
83+
84+
override string getShadowingKey(Member m) {
85+
not m instanceof Callable and result = ""
86+
or
87+
not m instanceof GeneratedConstructor and
88+
result =
89+
concat(int i, Parameter p |
90+
p = m.(Callable).getParameter(i)
91+
|
92+
p.getExternalNameNode().getValue(), "," order by i
93+
)
4094
}
4195

4296
override string getImplicitReceiverParameterName(Callable callable) {

0 commit comments

Comments
 (0)