Skip to content

Commit 1d10583

Browse files
committed
Unified: Add extensions convenience predicates
1 parent 5be6d86 commit 1d10583

7 files changed

Lines changed: 91 additions & 47 deletions

File tree

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

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
overlay[local?]
55
module;
66

7+
private import unified as U
78
private import AstPlugin
89
private import codeql.files.FileSystem
910
private import codeql.unified.internal.NameBinding as NameBinding
@@ -37,7 +38,11 @@ module Unified {
3738
)
3839
}
3940

40-
/** Gets the nearest enclosing class declaration, if any. */
41+
/**
42+
* Gets the nearest syntactically enclosing class declaration, if any.
43+
*
44+
* For the semantically enclosing class, use `getParentClass()` instead.
45+
*/
4146
ClassLikeDeclaration getEnclosingClass() {
4247
exists(AstNode parent | parent = this.getParent() |
4348
result = parent
@@ -47,6 +52,30 @@ module Unified {
4752
)
4853
}
4954

55+
/**
56+
* Gets the class that this node belongs to, if any.
57+
*
58+
* Unlike `getEnclosingClass`, this takes extensions into account:
59+
*
60+
* ```swift
61+
* class A {
62+
* let x : Int // belongs to class A
63+
* }
64+
*
65+
* extension A {
66+
* func foo() {} // also belongs to the class A
67+
* }
68+
* ```
69+
*/
70+
ClassLikeDeclaration getParentClass() {
71+
exists(ClassLikeDeclaration encl | encl = this.getEnclosingClass() |
72+
not exists(encl.getExtensionTarget()) and
73+
result = encl
74+
or
75+
result.getNameNode() = NameBinding::getStaticBindingTargetFromRef(encl.getExtensionTarget())
76+
)
77+
}
78+
5079
private AstNode overrideEnclosingCallableParent() {
5180
exists(FunctionExpr func |
5281
// Capture declarations are evaluated as part of the outer context, and
@@ -205,17 +234,36 @@ module Unified {
205234
/**
206235
* Gets the name of this member qualified by the short names of each immediately-enclosing class.
207236
*/
208-
string getQualifiedName() {
209-
exists(ClassLikeDeclaration cls |
210-
this = cls.getAMember() and
211-
result = cls.getQualifiedName() + "." + this.getNameOrFallback()
212-
)
237+
string getQualifiedName() { result = QualifiedName<getParentClass/1>::getQualifiedName(this) }
238+
}
239+
240+
private signature ClassLikeDeclaration getDeclaringClassSig(Member m);
241+
242+
private module QualifiedName<getDeclaringClassSig/1 getDeclaringClass> {
243+
string getQualifiedName(Member m) {
244+
not m instanceof U::EnumCaseConstructor and
245+
result = getQualifiedName(getDeclaringClass(m)) + "." + m.getNameOrFallback()
246+
or
247+
not exists(getDeclaringClass(m)) and
248+
result = m.getNameOrFallback()
213249
or
214-
not this = any(ClassLikeDeclaration cls).getAMember() and
215-
result = this.getNameOrFallback()
250+
result = getQualifiedName(m.(U::EnumCaseConstructor).getEnumCaseClass())
216251
}
217252
}
218253

254+
private ClassLikeDeclaration getParentClass(Member m) {
255+
result.getAMemberIncludingExtensions() = m
256+
}
257+
258+
private ClassLikeDeclaration getEnclosingClass(Member m) { result.getAMember() = m }
259+
260+
/**
261+
* Similar to `Member.getQualifiedName`, but avoids a dependency on name binding, making
262+
* it suitable for use in `toString` predicates.
263+
*/
264+
private predicate getQualifiedNameForToString =
265+
QualifiedName<getEnclosingClass/1>::getQualifiedName/1;
266+
219267
class ClassLikeDeclaration extends G::ClassLikeDeclaration {
220268
/** Gets the name of this declaration. */
221269
string getName() { result = this.getNameNode().getValue() }
@@ -226,8 +274,22 @@ module Unified {
226274
NameBinding::getStaticBindingTargetFromRef(this.getABaseType().getType())
227275
}
228276

277+
/**
278+
* Gets a member of this class, including members from extensions.
279+
*/
280+
Member getAMemberIncludingExtensions() {
281+
result = this.getAMember()
282+
or
283+
exists(ClassLikeDeclaration ext |
284+
this.getNameNode() = NameBinding::getStaticBindingTargetFromRef(ext.getExtensionTarget()) and
285+
result = ext.getAMember()
286+
)
287+
}
288+
229289
override string toString() {
230-
result = concat(getClassLikeDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
290+
result =
291+
concat(getClassLikeDeclarationKeyword(this) + " ") +
292+
concat(getQualifiedNameForToString(this))
231293
}
232294
}
233295

@@ -237,7 +299,8 @@ module Unified {
237299

238300
override string toString() {
239301
result =
240-
concat(getConstructorDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
302+
concat(getConstructorDeclarationKeyword(this) + " ") +
303+
concat(getQualifiedNameForToString(this))
241304
}
242305
}
243306

@@ -251,7 +314,9 @@ module Unified {
251314
string getName() { result = this.getNameNode().getValue() }
252315

253316
override string toString() {
254-
result = concat(getFunctionDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
317+
result =
318+
concat(getFunctionDeclarationKeyword(this) + " ") +
319+
concat(getQualifiedNameForToString(this))
255320
}
256321
}
257322

@@ -263,7 +328,9 @@ module Unified {
263328
string getName() { result = this.getNameNode().getValue() }
264329

265330
override string toString() {
266-
result = concat(getVariableDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
331+
result =
332+
concat(getVariableDeclarationKeyword(this) + " ") +
333+
concat(getQualifiedNameForToString(this))
267334
}
268335
}
269336

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
overlay[local?]
2+
module;
3+
14
private import unified
25
private import codeql.util.Unit
36
private import codeql.util.Option

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/**
22
* Provides Swift-specific name binding rules.
33
*/
4+
overlay[local?]
5+
module;
46

57
private import unified
68
private import codeql.unified.internal.StaticNameBinding

‎unified/ql/lib/codeql/unified/internal/mad/LegacyMaD.qll‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ private predicate callableSelector(
118118
name = callable.getName() and
119119
argLabels = getArgLabelsFromCallable(callable) and
120120
exists(ClassLikeDeclaration cls |
121-
callable = cls.getAMember() and
121+
callable = cls.getAMemberIncludingExtensions() and
122122
isSubclassOfType(cls, type)
123123
)
124124
}

‎unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ module Input3 implements InputSig3 {
784784
path.isEmpty()
785785
or
786786
exists(ClassLikeDeclaration c |
787-
c = n.(SuperExpr).getEnclosingClass() and
787+
c = n.(SuperExpr).getParentClass() and
788788
result = c.getBaseType(0).getType().(TypeMention).getTypeAt(path)
789789
)
790790
or

‎unified/ql/lib/utils/test/TestUtils.qll‎

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@ private import codeql.unified.internal.NameBinding
44
private import codeql.unified.internal.typeinference.TypeInference as TypeInference
55
private import codeql.unified.internal.typeinference.TypeInferencePlugin
66

7-
private string deriveClassName(ClassLikeDeclaration cls) {
8-
not exists(cls.getEnclosingClass()) and
9-
result = cls.getName()
10-
or
11-
result = getStaticBindingTargetFromRef(cls.getExtensionTarget()).getValue()
12-
or
13-
result = deriveClassName(cls.getEnclosingClass()) + "." + cls.getName()
14-
}
15-
167
private string getTupleFieldName(NameBinding decl) {
178
decl = any(TupleType tt).getField(result).getPattern()
189
}
@@ -24,11 +15,11 @@ private string defaultName(NameBinding decl) {
2415
not exists(getTupleFieldName(decl)) and name = decl.getName()
2516
|
2617
exists(ClassLikeDeclaration cls |
27-
decl.getDeclaration() = cls.getAMember() and
28-
result = deriveClassName(cls) + "." + name
18+
decl.getDeclaration() = cls.getAMemberIncludingExtensions() and
19+
result = cls.getQualifiedName() + "." + name
2920
)
3021
or
31-
not decl.getDeclaration() = any(ClassLikeDeclaration cls).getAMember() and
22+
not decl.getDeclaration() = any(ClassLikeDeclaration cls).getAMemberIncludingExtensions() and
3223
result = name
3324
)
3425
}
@@ -47,25 +38,6 @@ predicate nameBinding(NameBinding v, string alias) {
4738
)
4839
}
4940

50-
private string defaultCallableName(Callable c) {
51-
exists(TypeInference::Input3::Callable callable | callable.asCallable() = c |
52-
not c instanceof EnumConstructor and
53-
exists(ClassLikeDeclaration cls |
54-
c = cls.getAMember() and
55-
result = deriveClassName(cls) + "." + callable.getName()
56-
)
57-
or
58-
not c = any(ClassLikeDeclaration cls).getAMember() and
59-
result = callable.getName()
60-
or
61-
exists(ClassLikeDeclaration enum, EnumConstructor ctor |
62-
ctor = c and
63-
enum = ctor.getEnum() and
64-
result = deriveClassName(enum) + "." + ctor.getEnclosingClass().getName()
65-
)
66-
)
67-
}
68-
6941
private predicate callableAt(Callable c, string filepath, int line) {
7042
c.getLocation().hasLocationInfo(filepath, line, _, _, _)
7143
}
@@ -76,6 +48,6 @@ predicate callableName(Callable c, string alias) {
7648
keyValueCommentAt(filepath, line, "name", alias)
7749
or
7850
not keyValueCommentAt(filepath, line, "name", _) and
79-
alias = defaultCallableName(c)
51+
alias = c.(Member).getQualifiedName()
8052
)
8153
}

‎unified/ql/test/library-tests/controlflow/basicblock-slices.expected‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
| 8 | cfg.swift:8:1:8:6 | Double | 'Double -> Argument -V topLevelDecl -^ Double(...)' |
88
| 10 | cfg.swift:10:1:13:1 | enum MyError | 'enum MyError -V MyError -> BaseType -V Error' |
99
| 11 | cfg.swift:11:10:11:15 | MyError.error1 | 'MyError.error1 -V error1 -> MyError.error2 -V error2' |
10-
| 12 | cfg.swift:12:10:12:31 | MyError.error3 | 'MyError.error3 -V error3 -^ MyError.error3.(constructor)' |
10+
| 12 | cfg.swift:12:10:12:31 | MyError.error3 | 'MyError.error3 -V error3 -^ MyError.error3' |
1111
| 12 | cfg.swift:12:17:12:25 | withParam | 'withParam -? Block' |
1212
| 15 | cfg.swift:15:1:17:1 | func isZero | 'func isZero -V isZero' |
1313
| 15 | cfg.swift:15:13:15:13 | x | 'x -> Block' |

0 commit comments

Comments
 (0)