Skip to content

Commit 590903d

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

7 files changed

Lines changed: 96 additions & 47 deletions

File tree

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

Lines changed: 84 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,33 @@ 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+
exists(NameBinding::NamespaceNode ns |
76+
ns.isStaticMemberNamespace(result) and
77+
ns.getAnExtension().isStaticMemberNamespace(encl)
78+
)
79+
)
80+
}
81+
5082
private AstNode overrideEnclosingCallableParent() {
5183
exists(FunctionExpr func |
5284
// Capture declarations are evaluated as part of the outer context, and
@@ -205,17 +237,36 @@ module Unified {
205237
/**
206238
* Gets the name of this member qualified by the short names of each immediately-enclosing class.
207239
*/
208-
string getQualifiedName() {
209-
exists(ClassLikeDeclaration cls |
210-
this = cls.getAMember() and
211-
result = cls.getQualifiedName() + "." + this.getNameOrFallback()
212-
)
240+
string getQualifiedName() { result = QualifiedName<getParentClass/1>::getQualifiedName(this) }
241+
}
242+
243+
private signature ClassLikeDeclaration getDeclaringClassSig(Member m);
244+
245+
private module QualifiedName<getDeclaringClassSig/1 getDeclaringClass> {
246+
string getQualifiedName(Member m) {
247+
not m instanceof U::EnumCaseConstructor and
248+
result = getQualifiedName(getDeclaringClass(m)) + "." + m.getNameOrFallback()
249+
or
250+
not exists(getDeclaringClass(m)) and
251+
result = m.getNameOrFallback()
213252
or
214-
not this = any(ClassLikeDeclaration cls).getAMember() and
215-
result = this.getNameOrFallback()
253+
result = getQualifiedName(m.(U::EnumCaseConstructor).getEnumCaseClass())
216254
}
217255
}
218256

257+
private ClassLikeDeclaration getParentClass(Member m) {
258+
result.getAMemberIncludingExtensions() = m
259+
}
260+
261+
private ClassLikeDeclaration getEnclosingClass(Member m) { result.getAMember() = m }
262+
263+
/**
264+
* Similar to `Member.getQualifiedName`, but avoids a dependency on name binding, making
265+
* it suitable for use in `toString` predicates.
266+
*/
267+
private predicate getQualifiedNameForToString =
268+
QualifiedName<getEnclosingClass/1>::getQualifiedName/1;
269+
219270
class ClassLikeDeclaration extends G::ClassLikeDeclaration {
220271
/** Gets the name of this declaration. */
221272
string getName() { result = this.getNameNode().getValue() }
@@ -226,8 +277,24 @@ module Unified {
226277
NameBinding::getStaticBindingTargetFromRef(this.getABaseType().getType())
227278
}
228279

280+
/**
281+
* Gets a member of this class, including members from extensions.
282+
*/
283+
Member getAMemberIncludingExtensions() {
284+
result = this.getAMember() and
285+
not exists(this.getExtensionTarget())
286+
or
287+
exists(ClassLikeDeclaration ext, NameBinding::NamespaceNode ns |
288+
ns.isStaticMemberNamespace(this) and
289+
ns.getAnExtension().isStaticMemberNamespace(ext) and
290+
result = ext.getAMember()
291+
)
292+
}
293+
229294
override string toString() {
230-
result = concat(getClassLikeDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
295+
result =
296+
concat(getClassLikeDeclarationKeyword(this) + " ") +
297+
concat(getQualifiedNameForToString(this))
231298
}
232299
}
233300

@@ -237,7 +304,8 @@ module Unified {
237304

238305
override string toString() {
239306
result =
240-
concat(getConstructorDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
307+
concat(getConstructorDeclarationKeyword(this) + " ") +
308+
concat(getQualifiedNameForToString(this))
241309
}
242310
}
243311

@@ -251,7 +319,9 @@ module Unified {
251319
string getName() { result = this.getNameNode().getValue() }
252320

253321
override string toString() {
254-
result = concat(getFunctionDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
322+
result =
323+
concat(getFunctionDeclarationKeyword(this) + " ") +
324+
concat(getQualifiedNameForToString(this))
255325
}
256326
}
257327

@@ -263,7 +333,9 @@ module Unified {
263333
string getName() { result = this.getNameNode().getValue() }
264334

265335
override string toString() {
266-
result = concat(getVariableDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
336+
result =
337+
concat(getVariableDeclarationKeyword(this) + " ") +
338+
concat(getQualifiedNameForToString(this))
267339
}
268340
}
269341

‎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)