diff --git a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll index f82683c7f88a..c9a0c1b8ca82 100644 --- a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll +++ b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll @@ -4,6 +4,7 @@ overlay[local?] module; +private import unified as U private import AstPlugin private import codeql.files.FileSystem private import codeql.unified.internal.NameBinding as NameBinding @@ -37,7 +38,9 @@ module Unified { ) } - /** Gets the nearest enclosing class declaration, if any. */ + /** + * Gets the nearest syntactically enclosing class declaration, if any. + */ ClassLikeDeclaration getEnclosingClass() { exists(AstNode parent | parent = this.getParent() | result = parent @@ -205,17 +208,33 @@ module Unified { /** * Gets the name of this member qualified by the short names of each immediately-enclosing class. */ - string getQualifiedName() { - exists(ClassLikeDeclaration cls | - this = cls.getAMember() and - result = cls.getQualifiedName() + "." + this.getNameOrFallback() - ) + string getQualifiedName() { result = QualifiedName::getQualifiedName(this) } + } + + private signature ClassLikeDeclaration getDeclaringClassSig(Member m); + + private module QualifiedName { + string getQualifiedName(Member m) { + result = getQualifiedName(getDeclaringClass(m)) + "." + m.getNameOrFallback() or - not this = any(ClassLikeDeclaration cls).getAMember() and - result = this.getNameOrFallback() + not exists(getDeclaringClass(m)) and + result = m.getNameOrFallback() } } + private ClassLikeDeclaration getParentClass(Member m) { + result.getAMemberIncludingExtensions() = m + } + + private ClassLikeDeclaration getEnclosingClass(Member m) { result.getAMember() = m } + + /** + * Similar to `Member.getQualifiedName`, but avoids a dependency on name binding, making + * it suitable for use in `toString` predicates. + */ + private predicate getQualifiedNameForToString = + QualifiedName::getQualifiedName/1; + class ClassLikeDeclaration extends G::ClassLikeDeclaration { /** Gets the name of this declaration. */ string getName() { result = this.getNameNode().getValue() } @@ -226,8 +245,33 @@ module Unified { NameBinding::getStaticBindingTargetFromRef(this.getABaseType().getType()) } + /** + * Gets the target of this extension class, or this class itself if it is not an extension. + */ + ClassLikeDeclaration resolve() { + not exists(this.getExtensionTarget()) and + result = this + or + exists(NameBinding::NamespaceNode ns | + ns.isStaticMemberNamespace(result) and + ns.getAnExtension().isStaticMemberNamespace(this) + ) + } + + /** + * Gets a member of this class, including members from extensions. + */ + Member getAMemberIncludingExtensions() { + exists(ClassLikeDeclaration cls | + this = cls.resolve() and + result = cls.getAMember() + ) + } + override string toString() { - result = concat(getClassLikeDeclarationKeyword(this) + " ") + concat(this.getQualifiedName()) + result = + concat(getClassLikeDeclarationKeyword(this) + " ") + + concat(getQualifiedNameForToString(this)) } } @@ -237,7 +281,8 @@ module Unified { override string toString() { result = - concat(getConstructorDeclarationKeyword(this) + " ") + concat(this.getQualifiedName()) + concat(getConstructorDeclarationKeyword(this) + " ") + + concat(getQualifiedNameForToString(this)) } } @@ -251,7 +296,9 @@ module Unified { string getName() { result = this.getNameNode().getValue() } override string toString() { - result = concat(getFunctionDeclarationKeyword(this) + " ") + concat(this.getQualifiedName()) + result = + concat(getFunctionDeclarationKeyword(this) + " ") + + concat(getQualifiedNameForToString(this)) } } @@ -263,7 +310,9 @@ module Unified { string getName() { result = this.getNameNode().getValue() } override string toString() { - result = concat(getVariableDeclarationKeyword(this) + " ") + concat(this.getQualifiedName()) + result = + concat(getVariableDeclarationKeyword(this) + " ") + + concat(getQualifiedNameForToString(this)) } } diff --git a/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll b/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll index d2320f8cab1b..9057c512e41f 100644 --- a/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll +++ b/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll @@ -1,3 +1,6 @@ +overlay[local?] +module; + private import unified private import codeql.util.Unit private import codeql.util.Option diff --git a/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll index eecd22e22aab..103c8ff7d0a7 100644 --- a/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll @@ -1,6 +1,8 @@ /** * Provides Swift-specific name binding rules. */ +overlay[local?] +module; private import unified private import codeql.unified.internal.StaticNameBinding diff --git a/unified/ql/lib/codeql/unified/internal/mad/LegacyMaD.qll b/unified/ql/lib/codeql/unified/internal/mad/LegacyMaD.qll index 492ef6abc453..953e6b7d367a 100644 --- a/unified/ql/lib/codeql/unified/internal/mad/LegacyMaD.qll +++ b/unified/ql/lib/codeql/unified/internal/mad/LegacyMaD.qll @@ -118,7 +118,7 @@ private predicate callableSelector( name = callable.getName() and argLabels = getArgLabelsFromCallable(callable) and exists(ClassLikeDeclaration cls | - callable = cls.getAMember() and + callable = cls.getAMemberIncludingExtensions() and isSubclassOfType(cls, type) ) } diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll index 32a766650a33..c5572af2ae34 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll @@ -784,7 +784,7 @@ module Input3 implements InputSig3 { path.isEmpty() or exists(ClassLikeDeclaration c | - c = n.(SuperExpr).getEnclosingClass() and + c = n.(SuperExpr).getEnclosingClass().resolve() and result = c.getBaseType(0).getType().(TypeMention).getTypeAt(path) ) or diff --git a/unified/ql/lib/utils/test/TestUtils.qll b/unified/ql/lib/utils/test/TestUtils.qll index f99f24b8aabb..2b50c033878e 100644 --- a/unified/ql/lib/utils/test/TestUtils.qll +++ b/unified/ql/lib/utils/test/TestUtils.qll @@ -4,15 +4,6 @@ private import codeql.unified.internal.NameBinding private import codeql.unified.internal.typeinference.TypeInference as TypeInference private import codeql.unified.internal.typeinference.TypeInferencePlugin -private string deriveClassName(ClassLikeDeclaration cls) { - not exists(cls.getEnclosingClass()) and - result = cls.getName() - or - result = getStaticBindingTargetFromRef(cls.getExtensionTarget()).getValue() - or - result = deriveClassName(cls.getEnclosingClass()) + "." + cls.getName() -} - private string getTupleFieldName(NameBinding decl) { decl = any(TupleType tt).getField(result).getPattern() } @@ -24,11 +15,11 @@ private string defaultName(NameBinding decl) { not exists(getTupleFieldName(decl)) and name = decl.getName() | exists(ClassLikeDeclaration cls | - decl.getDeclaration() = cls.getAMember() and - result = deriveClassName(cls) + "." + name + decl.getDeclaration() = cls.getAMemberIncludingExtensions() and + result = cls.getQualifiedName() + "." + name ) or - not decl.getDeclaration() = any(ClassLikeDeclaration cls).getAMember() and + not decl.getDeclaration() = any(ClassLikeDeclaration cls).getAMemberIncludingExtensions() and result = name ) } @@ -47,25 +38,6 @@ predicate nameBinding(NameBinding v, string alias) { ) } -private string defaultCallableName(Callable c) { - exists(TypeInference::Input3::Callable callable | callable.asCallable() = c | - not c instanceof EnumConstructor and - exists(ClassLikeDeclaration cls | - c = cls.getAMember() and - result = deriveClassName(cls) + "." + callable.getName() - ) - or - not c = any(ClassLikeDeclaration cls).getAMember() and - result = callable.getName() - or - exists(ClassLikeDeclaration enum, EnumConstructor ctor | - ctor = c and - enum = ctor.getEnum() and - result = deriveClassName(enum) + "." + ctor.getEnclosingClass().getName() - ) - ) -} - private predicate callableAt(Callable c, string filepath, int line) { c.getLocation().hasLocationInfo(filepath, line, _, _, _) } @@ -76,6 +48,11 @@ predicate callableName(Callable c, string alias) { keyValueCommentAt(filepath, line, "name", alias) or not keyValueCommentAt(filepath, line, "name", _) and - alias = defaultCallableName(c) + ( + not c instanceof EnumCaseConstructor and + alias = c.(Member).getQualifiedName() + or + alias = c.(EnumCaseConstructor).getEnumCaseClass().getQualifiedName() + ) ) }