Skip to content

Commit 981fc4d

Browse files
committed
unified: Add language hook for inheritance restriction
1 parent 5b31814 commit 981fc4d

3 files changed

Lines changed: 44 additions & 1 deletion

File tree

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ class NameBindingPlugin extends Unit {
2525
*/
2626
bindingset[member, binding]
2727
predicate isPrivateToLocalScope(Stmt member, AstNode binding) { none() }
28+
29+
/**
30+
* Holds if `member` can be inherited by subclasses of `cls`.
31+
*
32+
* The caller has already restricted `member` to be a member of `cls`, and
33+
* ensured that `member` is a `VariableDeclaration` or `FunctionDeclaration`.
34+
*/
35+
bindingset[cls, member]
36+
predicate isInheritableMember(ClassLikeDeclaration cls, Member member) { none() }
2837
}
2938

3039
/** Holds if `member` is an instance member. */
@@ -35,6 +44,24 @@ predicate isInstanceMember(Member member) {
3544
)
3645
}
3746

47+
/**
48+
* Holds if `member` is a non-instance member declared in the context of a class or top-level.
49+
*/
50+
predicate isStaticMember(Member member) {
51+
exists(ClassLikeDeclaration cls | cls.getAMember() = member |
52+
not any(NameBindingPlugin p).isInstanceMember(cls, member)
53+
)
54+
or
55+
member = any(TopLevel t).getBody().getAStmt()
56+
}
57+
58+
/** Holds if `member` is an inheritable member. */
59+
predicate isInheritableMember(Member member) {
60+
exists(ClassLikeDeclaration cls | cls.getAMember() = member |
61+
any(NameBindingPlugin p).isInheritableMember(cls, member)
62+
)
63+
}
64+
3865
/** Holds if `binding` is only visible in its local scope. */
3966
pragma[nomagic]
4067
predicate isPrivateToLocalScope(AstNode binding) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ class NameBindingPluginSwift extends NameBindingPlugin {
2929
// Note: Private class members can be seen within type-extensions in the same file,
3030
// so we can't declare those private to their local scope.
3131
}
32+
33+
bindingset[cls, member]
34+
override predicate isInheritableMember(ClassLikeDeclaration cls, Member member) {
35+
exists(cls) and
36+
not member.hasModifier("private")
37+
}
3238
}
3339

3440
private predicate predefinedSourceFolders(string folder, int ordering) {

unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,15 @@ private predicate derivedStoreReadStep(NameBindingNode node1, NameBindingNode no
314314
)
315315
}
316316

317+
/** Holds if the member represented by `node` cannot be inherited. */
318+
pragma[nomagic]
319+
private predicate isInheritableMemberNode(NameBindingNode node) {
320+
exists(NameDeclaration decl |
321+
node.isIdentifier(decl) and
322+
isInheritableMember(decl.getDeclaration())
323+
)
324+
}
325+
317326
/** A name-binding node that has members. */
318327
class NamespaceNode extends NameBindingNode {
319328
NamespaceNode() {
@@ -363,7 +372,8 @@ class NamespaceNode extends NameBindingNode {
363372
result = this.getOwnMember(name)
364373
or
365374
not this.hasOwnMember(name) and
366-
result = this.getAnInheritanceParent().getMember(name)
375+
result = this.getAnInheritanceParent().getMember(name) and
376+
isInheritableMemberNode(result)
367377
}
368378
}
369379

0 commit comments

Comments
 (0)