Skip to content

Commit 4215c8a

Browse files
authored
Merge pull request #22867 from hvitved/unified/extensions-convenience
Unified: Add `extensions` convenience predicates
2 parents 4d89446 + ed5ee2b commit 4215c8a

6 files changed

Lines changed: 77 additions & 46 deletions

File tree

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

Lines changed: 61 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,9 @@ 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+
*/
4144
ClassLikeDeclaration getEnclosingClass() {
4245
exists(AstNode parent | parent = this.getParent() |
4346
result = parent
@@ -205,17 +208,33 @@ module Unified {
205208
/**
206209
* Gets the name of this member qualified by the short names of each immediately-enclosing class.
207210
*/
208-
string getQualifiedName() {
209-
exists(ClassLikeDeclaration cls |
210-
this = cls.getAMember() and
211-
result = cls.getQualifiedName() + "." + this.getNameOrFallback()
212-
)
211+
string getQualifiedName() { result = QualifiedName<getParentClass/1>::getQualifiedName(this) }
212+
}
213+
214+
private signature ClassLikeDeclaration getDeclaringClassSig(Member m);
215+
216+
private module QualifiedName<getDeclaringClassSig/1 getDeclaringClass> {
217+
string getQualifiedName(Member m) {
218+
result = getQualifiedName(getDeclaringClass(m)) + "." + m.getNameOrFallback()
213219
or
214-
not this = any(ClassLikeDeclaration cls).getAMember() and
215-
result = this.getNameOrFallback()
220+
not exists(getDeclaringClass(m)) and
221+
result = m.getNameOrFallback()
216222
}
217223
}
218224

225+
private ClassLikeDeclaration getParentClass(Member m) {
226+
result.getAMemberIncludingExtensions() = m
227+
}
228+
229+
private ClassLikeDeclaration getEnclosingClass(Member m) { result.getAMember() = m }
230+
231+
/**
232+
* Similar to `Member.getQualifiedName`, but avoids a dependency on name binding, making
233+
* it suitable for use in `toString` predicates.
234+
*/
235+
private predicate getQualifiedNameForToString =
236+
QualifiedName<getEnclosingClass/1>::getQualifiedName/1;
237+
219238
class ClassLikeDeclaration extends G::ClassLikeDeclaration {
220239
/** Gets the name of this declaration. */
221240
string getName() { result = this.getNameNode().getValue() }
@@ -226,8 +245,33 @@ module Unified {
226245
NameBinding::getStaticBindingTargetFromRef(this.getABaseType().getType())
227246
}
228247

248+
/**
249+
* Gets the target of this extension class, or this class itself if it is not an extension.
250+
*/
251+
ClassLikeDeclaration resolve() {
252+
not exists(this.getExtensionTarget()) and
253+
result = this
254+
or
255+
exists(NameBinding::NamespaceNode ns |
256+
ns.isStaticMemberNamespace(result) and
257+
ns.getAnExtension().isStaticMemberNamespace(this)
258+
)
259+
}
260+
261+
/**
262+
* Gets a member of this class, including members from extensions.
263+
*/
264+
Member getAMemberIncludingExtensions() {
265+
exists(ClassLikeDeclaration cls |
266+
this = cls.resolve() and
267+
result = cls.getAMember()
268+
)
269+
}
270+
229271
override string toString() {
230-
result = concat(getClassLikeDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
272+
result =
273+
concat(getClassLikeDeclarationKeyword(this) + " ") +
274+
concat(getQualifiedNameForToString(this))
231275
}
232276
}
233277

@@ -237,7 +281,8 @@ module Unified {
237281

238282
override string toString() {
239283
result =
240-
concat(getConstructorDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
284+
concat(getConstructorDeclarationKeyword(this) + " ") +
285+
concat(getQualifiedNameForToString(this))
241286
}
242287
}
243288

@@ -251,7 +296,9 @@ module Unified {
251296
string getName() { result = this.getNameNode().getValue() }
252297

253298
override string toString() {
254-
result = concat(getFunctionDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
299+
result =
300+
concat(getFunctionDeclarationKeyword(this) + " ") +
301+
concat(getQualifiedNameForToString(this))
255302
}
256303
}
257304

@@ -263,7 +310,9 @@ module Unified {
263310
string getName() { result = this.getNameNode().getValue() }
264311

265312
override string toString() {
266-
result = concat(getVariableDeclarationKeyword(this) + " ") + concat(this.getQualifiedName())
313+
result =
314+
concat(getVariableDeclarationKeyword(this) + " ") +
315+
concat(getQualifiedNameForToString(this))
267316
}
268317
}
269318

‎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).getEnclosingClass().resolve() and
788788
result = c.getBaseType(0).getType().(TypeMention).getTypeAt(path)
789789
)
790790
or

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

Lines changed: 9 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,11 @@ 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+
(
52+
not c instanceof EnumCaseConstructor and
53+
alias = c.(Member).getQualifiedName()
54+
or
55+
alias = c.(EnumCaseConstructor).getEnumCaseClass().getQualifiedName()
56+
)
8057
)
8158
}

0 commit comments

Comments
 (0)