diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index d2ec1f8d9908..47e1ebc7a8ec 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -79,6 +79,7 @@ supertypes: fields: parameter*: parameter body?: block + return_type?: expr # A member is anything that can appear in the body of a class-like declaration member: subtypes: diff --git a/unified/ql/lib/codeql/unified/internal/Ast.qll b/unified/ql/lib/codeql/unified/internal/Ast.qll index cd0f56770fde..9e8fb237ed2c 100644 --- a/unified/ql/lib/codeql/unified/internal/Ast.qll +++ b/unified/ql/lib/codeql/unified/internal/Ast.qll @@ -369,6 +369,9 @@ module Unified { /** Gets the node corresponding to the field `parameter`. */ F::Parameter getAParameter() { none() } + + /** Gets the node corresponding to the field `return_type`. */ + F::Expr getReturnType() { none() } } /** A class representing `catch_clause` nodes. */ @@ -721,7 +724,9 @@ module Unified { final override F::Parameter getAParameter() { result = this.getParameter(_) } /** Gets the node corresponding to the field `return_type`. */ - final F::Expr getReturnType() { unified_function_declaration_return_type(this, result) } + final override F::Expr getReturnType() { + unified_function_declaration_return_type(this, result) + } /** Gets the node corresponding to the field `type_constraint`. */ final F::TypeConstraint getTypeConstraint(int i) { @@ -782,7 +787,7 @@ module Unified { final override F::Parameter getAParameter() { result = this.getParameter(_) } /** Gets the node corresponding to the field `return_type`. */ - final F::Expr getReturnType() { unified_function_expr_return_type(this, result) } + final override F::Expr getReturnType() { unified_function_expr_return_type(this, result) } /** Gets a field or child node of this node. */ final override F::AstNode getAFieldOrChild() { diff --git a/unified/ql/lib/codeql/unified/internal/AstPlugin.qll b/unified/ql/lib/codeql/unified/internal/AstPlugin.qll index c57334d4143d..02f02f92628a 100644 --- a/unified/ql/lib/codeql/unified/internal/AstPlugin.qll +++ b/unified/ql/lib/codeql/unified/internal/AstPlugin.qll @@ -1,4 +1,4 @@ -overlay[local] +overlay[local?] module; private import unified @@ -23,10 +23,26 @@ class AstPlugin extends Unit { /** * Holds if the default constructor `cd` needs a parameter at index `i` with - * the given name. `i = 0` is reserved for the implicit receiver parameter. + * the given name. `hasDefault` indicates whether the parameter has a default + * value. + * + * `i = 0` is reserved for the implicit receiver parameter. */ bindingset[cd] - predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { none() } + predicate defaultConstructorParameter( + ConstructorDeclaration cd, int i, string name, boolean hasDefault + ) { + none() + } + + /** + * Holds if the type of the `i`th parameter of the default constructor `cd` + * is `type`. + */ + bindingset[cd] + predicate defaultConstructorParameterType(ConstructorDeclaration cd, int i, AstNode type) { + none() + } } bindingset[f] @@ -50,6 +66,13 @@ string getVariableDeclarationKeyword(VariableDeclaration decl) { } bindingset[cd] -predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { - any(AstPlugin p).defaultConstructorParameter(cd, i, name) +predicate defaultConstructorParameter( + ConstructorDeclaration cd, int i, string name, boolean hasDefault +) { + any(AstPlugin p).defaultConstructorParameter(cd, i, name, hasDefault) +} + +bindingset[cd] +predicate defaultConstructorParameterType(ConstructorDeclaration cd, int i, AstNode type) { + any(AstPlugin p).defaultConstructorParameterType(cd, i, type) } diff --git a/unified/ql/lib/codeql/unified/internal/AstPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/AstPluginSwift.qll index 6a46be6e3816..ff2d03fb9b01 100644 --- a/unified/ql/lib/codeql/unified/internal/AstPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/AstPluginSwift.qll @@ -1,9 +1,36 @@ -overlay[local] +overlay[local?] module; private import unified private import AstPlugin +private predicate structHasParameterForField( + ConstructorDeclaration cd, VariableDeclaration decl, int i, string name, boolean hasDefault +) { + exists(ClassLikeDeclaration struct | + struct.hasModifier("struct") and + cd = struct.getAMember() and + decl = struct.getMember(i) and + not decl.hasModifier("static") and + name = decl.getPattern().(Identifier).getValue() + | + // if `decl` has an initializer or `Optional` type then the parameter has a default value + decl.hasModifier("var") and + if + exists(decl.getValue()) + or + // we could make this more precise using static name binding, but for now we + // use a simpler approach to avoid the dependency on static name binding + decl.getType().(GenericTypeExpr).getBase().(Identifier).getValue() = "Optional" + then hasDefault = true + else hasDefault = false + or + decl.hasModifier("let") and + not exists(decl.getValue()) and + hasDefault = false + ) +} + private class AstPluginSwift extends AstPlugin { bindingset[f] override string getFunctionDeclarationKeyword(FunctionDeclaration f) { @@ -27,25 +54,19 @@ private class AstPluginSwift extends AstPlugin { } bindingset[cd] - override predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { - exists(ClassLikeDeclaration cls | - cd = cls.getAMember() and - cls.hasModifier("struct") and - name = - rank[i](VariableDeclaration decl, int j, string s | - decl = cls.getMember(j) and - not decl.hasModifier("static") and - ( - // if `decl` has an initializer then this parameter has that initializer as a default value - decl.hasModifier("var") - or - decl.hasModifier("let") and - not exists(decl.getValue()) - ) and - s = decl.getPattern().(Identifier).getValue() - | - s order by j - ) + override predicate defaultConstructorParameter( + ConstructorDeclaration cd, int i, string name, boolean hasDefault + ) { + name = rank[i](int j, string s | structHasParameterForField(cd, _, j, s, _) | s order by j) and + structHasParameterForField(cd, _, _, name, hasDefault) + } + + bindingset[cd] + override predicate defaultConstructorParameterType(ConstructorDeclaration cd, int i, AstNode type) { + exists(VariableDeclaration decl, string name | + this.defaultConstructorParameter(cd, i, name, _) and + structHasParameterForField(cd, decl, _, name, _) and + type = decl.getType() ) } } diff --git a/unified/ql/lib/codeql/unified/internal/ParameterEx.qll b/unified/ql/lib/codeql/unified/internal/ParameterEx.qll index f9caa38d05e2..0ec703cb2b66 100644 --- a/unified/ql/lib/codeql/unified/internal/ParameterEx.qll +++ b/unified/ql/lib/codeql/unified/internal/ParameterEx.qll @@ -11,9 +11,9 @@ private newtype TParameterEx = TImplicitReceiverParameter(Callable c) { exists(LocalVariable v | v.isImplicitReceiverParameter(c)) } or - TDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { + TDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name, boolean hasDefault) { cd.hasModifier("generated") and - defaultConstructorParameter(cd, i, name) + defaultConstructorParameter(cd, i, name, hasDefault) } final class ParameterEx = ParameterExImpl; @@ -42,7 +42,7 @@ abstract private class ParameterExImpl extends TParameterEx { * constructor `cd` with name `name`. */ predicate isDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { - this = TDefaultConstructorParameter(cd, i, name) + this = TDefaultConstructorParameter(cd, i, name, _) } /** Gets the callable that this parameter belongs to. */ @@ -60,6 +60,12 @@ abstract private class ParameterExImpl extends TParameterEx { this.isDefaultConstructorParameter(result, _, _) } + /** Holds if this parameter has a default value. */ + abstract predicate hasDefault(); + + /** Gets the type declaration of this parameter, if any. */ + abstract AstNode getType(); + /** Gets a textual representation of this parameter. */ abstract string toString(); @@ -72,6 +78,10 @@ private class AstParameterEx extends ParameterExImpl, TAstParameter { AstParameterEx() { this = TAstParameter(p) } + override predicate hasDefault() { exists(p.getDefault()) } + + override AstNode getType() { result = p.getType() } + override string toString() { result = p.toString() } override Location getLocation() { result = p.getLocation() } @@ -82,6 +92,10 @@ private class ImplicitReceiverParameterEx extends ParameterExImpl, TImplicitRece ImplicitReceiverParameterEx() { this = TImplicitReceiverParameter(c) } + override predicate hasDefault() { none() } + + override AstNode getType() { none() } + override string toString() { exists(LocalVariable v | v.isImplicitReceiverParameter(c) and @@ -96,8 +110,13 @@ private class DefaultConstructorParameterEx extends ParameterExImpl, TDefaultCon ConstructorDeclaration cd; int i; string name; + boolean hasDefault; + + DefaultConstructorParameterEx() { this = TDefaultConstructorParameter(cd, i, name, hasDefault) } + + override AstNode getType() { defaultConstructorParameterType(cd, i, result) } - DefaultConstructorParameterEx() { this = TDefaultConstructorParameter(cd, i, name) } + override predicate hasDefault() { hasDefault = true } override string toString() { result = name + " [" + cd.getName() + " default constructor]" } diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll b/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll index eb6c3fa85608..934b6d90f6fc 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll @@ -1,4 +1,6 @@ /** Provides classes representing types without type arguments. */ +overlay[local?] +module; import unified // reexport in order to shadow e.g. the `TypeParameter` class private import unified as Unified diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll index 03abcd44f807..5bd2d7f885bc 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll @@ -21,6 +21,7 @@ private import codeql.util.Unit private import codeql.typeinference.internal.TypeInference +private import codeql.unified.internal.ParameterEx private import codeql.unified.internal.ExprPositions private import codeql.unified.internal.NameBinding private import utils.test.InlineExpectationsTest @@ -116,7 +117,7 @@ private module M2 = Make2; import M2 -private module Input3 implements InputSig3 { +module Input3 implements InputSig3 { private import unified as Unified predicate cacheRevRef() { @@ -181,152 +182,10 @@ private module Input3 implements InputSig3 { Expr getExpr() { none() } } - /** - * Provides declarations with explicit AST node representations. - * - * Most declarations are explicitly declared, but for example implicit - * receiver parameters are not. - */ - private module AstDeclaration { - abstract class Declaration extends AstNode { - abstract TypeMention getDeclaringType(); - - abstract TypeMention getType(); - - abstract Identifier getNameNode(); - - pragma[nomagic] - predicate isMemberOf(ClassLikeDeclaration cls, Identifier i, string name) { - exists(NamespaceNode ns | - ns.isInstanceMemberNamespace(cls) and - ns.getMember(name).isIdentifier(i) and - i = this.getNameNode() - ) - } - } - - abstract class VariableDeclaration extends Declaration { - abstract AstNode getPattern(); - - abstract AstNode getInitializer(); - - predicate isCoercionSite() { none() } - - override TypeMention getDeclaringType() { none() } - - override Identifier getNameNode() { result = this.getPattern() } - } - - private class OrdinaryVariableDeclaration extends VariableDeclaration instanceof Unified::VariableDeclaration - { - override AstNode getPattern() { result = Unified::VariableDeclaration.super.getPattern() } - - override AstNode getInitializer() { result = super.getValue() } - - override TypeMention getType() { result = Unified::VariableDeclaration.super.getType() } - } - - class Field extends OrdinaryVariableDeclaration { - ClassLikeDeclaration cls; - - Field() { this = cls.getAMember() } - - override TypeMention getDeclaringType() { result = getClassLikeDeclarationTypeMention(cls) } - } - - private class EnumField_ extends Field instanceof EnumField { - override TypeMention getType() { result = super.getEnum().getNameNode() } - } - - class Parameter extends VariableDeclaration instanceof Unified::Parameter { - override AstNode getInitializer() { none() } - - override AstNode getPattern() { result = Unified::Parameter.super.getPattern() } - - override TypeMention getType() { result = Unified::Parameter.super.getType() } - } - - abstract class Callable extends Declaration instanceof Unified::Callable { - abstract TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp); - - override TypeMention getDeclaringType() { - exists(ClassLikeDeclaration c | - result = getClassLikeDeclarationTypeMention(c) and - this = c.getAMember() - ) - } - - abstract TypeParameter getTypeParameter(int i); - - abstract Parameter getParameter(int i); - - abstract AstNode getBody(); - } - - private class FunctionDeclarationCallable extends Callable instanceof FunctionDeclaration { - override TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { - // todo: type equalities? - exists(BoundTypeConstraint constraint | - constraint = super.getATypeConstraint() and - tp = constraint.getType().(TypeMention).getType() and - result = constraint.getBound() - ) - } - - override TypeParameterType getTypeParameter(int i) { - result.getTypeParameter() = FunctionDeclaration.super.getTypeParameter(i) - } - - override Parameter getParameter(int i) { result = FunctionDeclaration.super.getParameter(i) } - - override TypeMention getType() { result = super.getReturnType() } - - override AstNode getBody() { result = FunctionDeclaration.super.getBody() } - - override Identifier getNameNode() { result = FunctionDeclaration.super.getNameNode() } - } - - class ConstructorDeclarationCallable extends Callable instanceof ConstructorDeclaration { - override TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { none() } - - override TypeParameterType getTypeParameter(int i) { none() } - - override Parameter getParameter(int i) { - result = ConstructorDeclaration.super.getParameter(i) - } - - override TypeMention getType() { result = this.getDeclaringType() } - - override AstNode getBody() { result = ConstructorDeclaration.super.getBody() } - - override Identifier getNameNode() { result = ConstructorDeclaration.super.getNameNode() } - } - - private class EnumConstructorCallable extends ConstructorDeclarationCallable instanceof EnumConstructor - { - override TypeMention getDeclaringType() { result = super.getEnum().getNameNode() } - - override Identifier getNameNode() { result = EnumConstructor.super.getNameNode() } - } - - class Closure extends Expr, Callable instanceof Unified::FunctionExpr { - override TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { none() } - - override TypeParameterType getTypeParameter(int i) { none() } - - override Parameter getParameter(int i) { result = FunctionExpr.super.getParameter(i) } - - override TypeMention getType() { result = super.getReturnType() } - - override AstNode getBody() { result = FunctionExpr.super.getBody() } - - override Identifier getNameNode() { none() } - } - } - private newtype TDeclaration = - TExplicitDeclaration(AstDeclaration::Declaration decl) or - TImplicitParameterDeclaration(LocalVariable v) { v.isImplicitReceiverParameter(_) } + TAstVariableDeclaration(Unified::VariableDeclaration decl) or + TCallableDeclaration(Unified::Callable c) or + TParameterExDeclaration(ParameterEx p) final class Declaration = DeclarationImpl; @@ -340,24 +199,17 @@ private module Input3 implements InputSig3 { abstract string toString(); abstract Location getLocation(); - } - - private class ExplicitDeclarationImpl extends DeclarationImpl, TExplicitDeclaration { - AstDeclaration::Declaration decl; - - ExplicitDeclarationImpl() { this = TExplicitDeclaration(decl) } - - AstDeclaration::Declaration getAstNode() { result = decl } - - override TypeMention getDeclaringType() { result = decl.getDeclaringType() } - override TypeMention getType() { result = decl.getType() } - - override Identifier getNameNode() { result = decl.getNameNode() } - - override string toString() { result = decl.toString() } + pragma[nomagic] + predicate isInstanceMemberOf(ClassLikeDeclaration cls, string name) { + exists(NamespaceNode ns, Identifier i | + ns.isInstanceMemberNamespace(cls) and + ns.getMember(name).isIdentifier(i) and + i = this.getNameNode() + ) + } - override Location getLocation() { result = decl.getLocation() } + Unified::Callable asCallable() { this = TCallableDeclaration(result) } } class Variable extends LocalNameBindingOutput::Local { @@ -374,18 +226,40 @@ private module Input3 implements InputSig3 { predicate preservesInitializerType() { any() } } - private class ExplicitVariableDeclarationImpl extends VariableDeclarationImpl, - ExplicitDeclarationImpl - { - override AstDeclaration::VariableDeclaration decl; + private class AstVariableDeclaration extends VariableDeclarationImpl, TAstVariableDeclaration { + Unified::VariableDeclaration decl; + + AstVariableDeclaration() { this = TAstVariableDeclaration(decl) } + + Unified::VariableDeclaration getDecl() { result = decl } + + override TypeMention getDeclaringType() { none() } + + override TypeMention getType() { result = decl.getType() } + + override Identifier getNameNode() { result = decl.getPattern() } override AstNode getPattern() { result = decl.getPattern() } - override AstNode getInitializer() { result = decl.getInitializer() } + override AstNode getInitializer() { result = decl.getValue() } + + override string toString() { result = decl.toString() } + + override Location getLocation() { result = decl.getLocation() } + } + + class Field extends AstVariableDeclaration { + ClassLikeDeclaration cls; + + Field() { decl = cls.getAMember() } + + override TypeMention getDeclaringType() { result = getClassLikeDeclarationTypeMention(cls) } } - class Field extends ExplicitDeclarationImpl { - override AstDeclaration::Field decl; + private class EnumField_ extends Field { + override EnumField decl; + + override TypeMention getType() { result = decl.getEnum().getNameNode() } } abstract class FieldAccess extends Expr { @@ -401,10 +275,10 @@ private module Input3 implements InputSig3 { } pragma[nomagic] - private Identifier lookupMember(MemberAccessExpr mae) { - exists(ClassLikeDeclaration cls, ExplicitDeclarationImpl decl, string name | + private Declaration lookupInstanceMember(MemberAccessExpr mae) { + exists(ClassLikeDeclaration cls, string name | lookupMember0(mae, cls, name) and - decl.getAstNode().isMemberOf(cls, result, name) + result.isInstanceMemberOf(cls, name) ) } @@ -419,12 +293,12 @@ private module Input3 implements InputSig3 { override Field getField() { // mutual recursion; resolving qualified fields requires resolving types and vice versa - result.getNameNode() = lookupMember(this) + result = lookupInstanceMember(this) or // also mutual recursion exists(TupleType tuple, string name | this.isTupleProjection(tuple, name) and - result.getAstNode() = tuple.getField(name) + result.getDecl() = tuple.getField(name) ) or // no mutual recursion; can be resolved directly with static name binding @@ -433,8 +307,9 @@ private module Input3 implements InputSig3 { } private Type getImplicitReceiverType(UnqualifiedMemberAccess uma, TypePath path) { - exists(ImplicitParameterDeclarationImpl p | - p.getVariable() = uma.getImplicitQualifierVariable() and + exists(Parameter p, Unified::Callable c | + p.getParameterEx().isImplicitReceiverParameter(c) and + c = uma.getImplicitQualifierVariable().getDeclaringCallable() and result = p.getType().getTypeAt(path) ) } @@ -464,74 +339,121 @@ private module Input3 implements InputSig3 { Expr getExpr() { result = this.getValue() } } - final class Parameter = ParameterImpl; + class Parameter extends VariableDeclarationImpl, TParameterExDeclaration { + ParameterEx p; - abstract private class ParameterImpl extends VariableDeclarationImpl { } + Parameter() { this = TParameterExDeclaration(p) } - private class ExplicitParameterImpl extends ParameterImpl, ExplicitDeclarationImpl { - override AstDeclaration::Parameter decl; + ParameterEx getParameterEx() { result = p } - override AstNode getPattern() { result = decl.getPattern() } - - override AstNode getInitializer() { result = decl.getInitializer() } - } - - private class ImplicitParameterDeclarationImpl extends ParameterImpl, - TImplicitParameterDeclaration - { - LocalVariable v; - - ImplicitParameterDeclarationImpl() { this = TImplicitParameterDeclaration(v) } - - LocalVariable getVariable() { result = v } - - override AstNode getPattern() { none() } + override AstNode getPattern() { result = p.asParameter().getPattern() } override AstNode getInitializer() { none() } override TypeMention getDeclaringType() { none() } override TypeMention getType() { - result = v.getDeclaringCallable().(AstDeclaration::Callable).getDeclaringType() + result = p.getType() + or + exists(Callable c | + p.isImplicitReceiverParameter(_) and + this = c.getParameter(_) and + result = c.getDeclaringType() + ) } override Identifier getNameNode() { none() } - override string toString() { result = v.toString() } + override string toString() { result = p.toString() } - override Location getLocation() { result = v.getLocation() } + override Location getLocation() { result = p.getLocation() } } predicate implicitParameterDecl(Parameter p, Variable v) { - v = p.(ImplicitParameterDeclarationImpl).getVariable() + exists(Unified::Callable c | + p.getParameterEx().isImplicitReceiverParameter(c) and + v.(LocalVariable).isImplicitReceiverParameter(c) + ) } - class Callable extends ExplicitDeclarationImpl { - override AstDeclaration::Callable decl; + class Callable extends DeclarationImpl, TCallableDeclaration { + Unified::Callable c; + + Callable() { this = TCallableDeclaration(c) } + + Unified::Callable getCallable() { result = c } + + override TypeMention getDeclaringType() { + result = c.(EnumConstructor).getEnum().getNameNode() + or + not c instanceof EnumConstructor and + exists(ClassLikeDeclaration cls | + c = cls.getAMember() and + result = getClassLikeDeclarationTypeMention(cls) + ) + } + + override TypeMention getType() { + result = c.getReturnType() + or + c instanceof ConstructorDeclaration and + result = this.getDeclaringType() + } + + override Identifier getNameNode() { + result = c.(AccessorDeclaration).getNameNode() + or + result = c.(FunctionDeclaration).getNameNode() + or + result = c.(EnumConstructor).getEnclosingClass().getNameNode() + or + not c instanceof EnumConstructor and + result = c.(ConstructorDeclaration).getNameNode() + } + + string getName() { + result = this.getNameNode().getValue() + or + c instanceof DestructorDeclaration and + result = "" + or + c instanceof InitializerDeclaration and + result = "" + } - TypeParameter getTypeParameter(int i) { result = decl.getTypeParameter(i) } + TypeParameter getTypeParameter(int i) { + result = TTypeParameterType(c.(FunctionDeclaration).getTypeParameter(i)) + } TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { - result = decl.getAdditionalTypeParameterConstraint(tp) + // todo: type equalities? + exists(BoundTypeConstraint constraint | + constraint = c.(FunctionDeclaration).getATypeConstraint() and + tp = constraint.getType().(TypeMention).getType() and + result = constraint.getBound() + ) } Parameter getParameter(int i) { - result = TExplicitDeclaration(decl.getParameter(i - 1)) - or - i = 0 and - exists(LocalVariable v | - result = TImplicitParameterDeclaration(v) and - v.isImplicitReceiverParameter(decl) + exists(ParameterEx param | param = result.getParameterEx() | + param.isImplicitReceiverParameter(c) and + i = 0 + or + param.asParameter() = c.getParameter(i - 1) + or + param.isDefaultConstructorParameter(c, i, _) ) } - AstNode getBody() { result = decl.getBody() } - } + AstNode getBody() { result = c.getBody() } - Callable getEnclosingCallable(AstNode node) { - result = TExplicitDeclaration(node.getEnclosingCallable()) + override string toString() { result = c.toString() } + + override Location getLocation() { result = c.getLocation() } } + Callable getEnclosingCallable(AstNode node) { result.asCallable() = node.getEnclosingCallable() } + class InvocationResolutionContext = Unit; class Invocation extends Expr, CallExpr { @@ -566,27 +488,27 @@ private module Input3 implements InputSig3 { result = getFunctionInvoke(t) } - private Unified::Callable getTargetViaStaticNameBinding() { + private Callable getTargetViaStaticNameBinding() { exists(NameBinding b | b = getStaticBindingTargetFromRef(this.getCallee()) | // object creation (including enum constructors): `String(42)`, `Optional.Some(42)` exists(ClassLikeDeclaration cls, ConstructorDeclaration init | cls.getNameNode() = b and init = cls.getAMember() and - result = init + result.asCallable() = init ) or // call without explicit receiver: `foo(42)` - result.(AstDeclaration::Declaration).getNameNode() = b + result.getNameNode() = b ) } - Unified::Callable getTargetImpl(boolean isFunctionExprInvoke) { + Callable getTargetImpl(boolean isFunctionExprInvoke) { // mutual recursion; call with explicit receiver: `obj.foo(42)` - result.(AstDeclaration::Declaration).getNameNode() = lookupMember(this.getCallee()) and + result = lookupInstanceMember(this.getCallee()) and isFunctionExprInvoke = false or // mutual recursion; call to a function expression: `callback(42)` - result = this.getInvokeTarget(_) and + result.asCallable() = this.getInvokeTarget(_) and isFunctionExprInvoke = true or // no mutual recursion; can be resolved directly with static name binding @@ -595,13 +517,11 @@ private module Input3 implements InputSig3 { } Callable getTarget(InvocationResolutionContext c) { - result.getAstNode() = this.getTargetImpl(_) and + result = this.getTargetImpl(_) and exists(c) } - Callable getATargetForTypeQualifierMatching() { - result.getAstNode() = this.getTargetViaStaticNameBinding() - } + Callable getATargetForTypeQualifierMatching() { result = this.getTargetViaStaticNameBinding() } } Type inferInvocationArgumentType( @@ -627,7 +547,7 @@ private module Input3 implements InputSig3 { result = inferType(invocation.getArgument(i), path) or i = 0 and - result = getImplicitReceiverType(invocation.(CallExpr).getCallee(), path) + result = getImplicitReceiverType(invocation.getCallee(), path) ) ) } @@ -640,23 +560,25 @@ private module Input3 implements InputSig3 { result = M3::inferInvocationTypeDefault(invocation, _, path) } - class Closure extends Callable, ExplicitDeclarationImpl { - override AstDeclaration::Closure decl; + class Closure extends Callable { + private FunctionExpr fe; + + Closure() { fe = this.getCallable() } - Expr getDefiningExpr() { result = decl } + Expr getDefiningExpr() { result = fe } } class ClosureParameterPseudoType extends T::ClosureParameterPseudoType { - Parameter getParameter() { result = TExplicitDeclaration(this.getParam()) } + Parameter getParameter() { result.getParameterEx().asParameter() = this.getParam() } } pragma[nomagic] - Type getClosureType(Closure c) { result = getFunctionExprType(c.getAstNode()) } + Type getClosureType(Closure c) { result = getFunctionExprType(c.getDefiningExpr()) } pragma[nomagic] TypePath getClosureParameterTypePath(Parameter p) { exists(FunctionExpr fe, Type t, int i | - p = TExplicitDeclaration(fe.getParameter(i)) and + p.getParameterEx().asParameter() = fe.getParameter(i) and t = getFunctionExprType(fe) and result = getFunctionExprParameterTypePath(t, fe.getNumberOfParameters(), i) ) @@ -715,9 +637,8 @@ private module Input3 implements InputSig3 { ) or // `Optional.none` has an unknown `Wrapper` type - exists(Field f, TypeParameter tp | + exists(EnumField_ f, TypeParameter tp | n.(FieldAccess).getField() = f and - f.(ExplicitDeclarationImpl).getAstNode() instanceof EnumField and tp = f.getType().getType().getATypeParameter() and path = TypePath::singleton(tp) and // `Optional.none` does not have an unknown `Wrapper` type @@ -756,7 +677,7 @@ module CachedStage = M3::CachedStage; cached VariableDeclaration resolveFieldAccess(Expr access) { CachedStage::ref() and - result = access.(Input3::FieldAccess).getField().getAstNode() + result = access.(Input3::FieldAccess).getField().getDecl() } /** @@ -767,7 +688,7 @@ VariableDeclaration resolveFieldAccess(Expr access) { cached Callable resolveCallTarget(CallExpr ce) { CachedStage::ref() and - result = ce.(Input3::Invocation).getTargetImpl(_) + result = ce.(Input3::Invocation).getTargetImpl(_).asCallable() } private predicate typeTestAstNodeRepr(AstNode n, string repr) { diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll index 8ce663ed17b8..2ea9ccd4afdc 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll @@ -1,6 +1,8 @@ /** * Provides Swift-specific type inference logic. */ +overlay[local?] +module; private import Type private import TypeInference @@ -11,34 +13,46 @@ private class StructType extends ClassLikeDeclarationType { StructType() { c.getAModifier().getValue() = "struct" } } -private class BuiltinType extends StructType { +private class EnumType extends ClassLikeDeclarationType { + EnumType() { c.getAModifier().getValue() = "enum" } +} + +private class BuiltinType extends ClassLikeDeclarationType { BuiltinType() { this.getClassLikeDeclaration() instanceof BuiltinClassLikeDeclaration } } -private class BoolType extends BuiltinType { +private class BuiltinStructType extends BuiltinType, StructType { } + +private class BuiltinEnumType extends BuiltinType, EnumType { } + +private class BoolType extends BuiltinStructType { BoolType() { this.getName() = "Bool" } } -private class IntType extends BuiltinType { +private class IntType extends BuiltinStructType { IntType() { this.getName() = "Int" } } -private class DoubleType extends BuiltinType { +private class DoubleType extends BuiltinStructType { DoubleType() { this.getName() = "Double" } } -private class StringType extends BuiltinType { +private class StringType extends BuiltinStructType { StringType() { this.getName() = "String" } } -private class ArrayType extends BuiltinType { +private class ArrayType extends BuiltinStructType { ArrayType() { this.getName() = "Array" } } -class FunctionType extends BuiltinType { +class FunctionType extends BuiltinStructType { FunctionType() { this.getName() = "Function" } } +class OptionalType extends BuiltinEnumType { + OptionalType() { this.getName() = "Optional" } +} + pragma[nomagic] TypeParameter getFunctionArgsTypeParameter() { result = any(FunctionType t).getPositionalTypeParameter(0) @@ -62,7 +76,7 @@ private TypePath functionReturnPath() { result = TypePath::singleton(getFunctionReturnTypeParameter()) } -private class TupleType extends BuiltinType { +private class TupleType extends BuiltinStructType { private int arity; TupleType() { arity = this.getName().regexpCapture("Tuple([0-9]+)", 1).toInt() } diff --git a/unified/ql/lib/utils/test/TestUtils.qll b/unified/ql/lib/utils/test/TestUtils.qll index 2697ece6d30f..f99f24b8aabb 100644 --- a/unified/ql/lib/utils/test/TestUtils.qll +++ b/unified/ql/lib/utils/test/TestUtils.qll @@ -1,6 +1,7 @@ private import unified private import CommentUtil 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) { @@ -46,32 +47,22 @@ predicate nameBinding(NameBinding v, string alias) { ) } -private string getCallableName(Callable c) { - result = c.(AccessorDeclaration).getName() - or - result = c.(ConstructorDeclaration).getName() - or - c instanceof DestructorDeclaration and - result = "" - or - result = c.(FunctionDeclaration).getName() - or - c instanceof InitializerDeclaration and - result = "" -} - private string defaultCallableName(Callable c) { - exists(ClassLikeDeclaration cls | - c = cls.getAMember() and - result = deriveClassName(cls) + "." + getCallableName(c) - ) - or - not c = any(ClassLikeDeclaration cls).getAMember() and - result = getCallableName(c) - or - exists(ClassLikeDeclaration enum | - enum = c.(EnumConstructor).getEnum() and - result = deriveClassName(enum) + "." + c.getEnclosingClass().getName() + 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() + ) ) } diff --git a/unified/ql/test/library-tests/constructors/constructors.swift b/unified/ql/test/library-tests/constructors/constructors.swift index a106052f5c6a..2e03b1092fcf 100644 --- a/unified/ql/test/library-tests/constructors/constructors.swift +++ b/unified/ql/test/library-tests/constructors/constructors.swift @@ -36,8 +36,8 @@ class C8: C7 { // inherits `init(x: String, y: Int)` } } -struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double, f6: Double, f7: Double = 0)` - var f1: Int = 0 +struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double, f6: Double, f7: Double = 0, f8: Int? = nil)` + var f1 = 0 var f2: String let f3: Bool = false let f4: Double @@ -58,4 +58,5 @@ struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double, f6: Double, print("Changed from \(oldValue) to \(f7)") } } + var f8: Int? } diff --git a/unified/ql/test/library-tests/constructors/test.expected b/unified/ql/test/library-tests/constructors/test.expected index 9498639dd075..cbbb9b32e04b 100644 --- a/unified/ql/test/library-tests/constructors/test.expected +++ b/unified/ql/test/library-tests/constructors/test.expected @@ -21,8 +21,14 @@ implicitReceiverParameter | constructors.swift:49:9:51:9 | self | constructors.swift:49:9:51:9 | AccessorDeclaration | | constructors.swift:54:9:56:9 | self | constructors.swift:54:9:56:9 | AccessorDeclaration | | constructors.swift:57:9:59:9 | self | constructors.swift:57:9:59:9 | AccessorDeclaration | -callableExDefaultConstructorParameter -| constructors.swift:39:1:39:0 | init | 1 | constructors.swift:39:1:39:0 | f1 [init default constructor] | f1 | -| constructors.swift:39:1:39:0 | init | 2 | constructors.swift:39:1:39:0 | f2 [init default constructor] | f2 | -| constructors.swift:39:1:39:0 | init | 3 | constructors.swift:39:1:39:0 | f4 [init default constructor] | f4 | -| constructors.swift:39:1:39:0 | init | 4 | constructors.swift:39:1:39:0 | f7 [init default constructor] | f7 | +defaultConstructorParameter +| constructors.swift:39:1:39:0 | init | 1 | constructors.swift:39:1:39:0 | f1 [init default constructor] | f1 | true | +| constructors.swift:39:1:39:0 | init | 2 | constructors.swift:39:1:39:0 | f2 [init default constructor] | f2 | false | +| constructors.swift:39:1:39:0 | init | 3 | constructors.swift:39:1:39:0 | f4 [init default constructor] | f4 | false | +| constructors.swift:39:1:39:0 | init | 4 | constructors.swift:39:1:39:0 | f7 [init default constructor] | f7 | true | +| constructors.swift:39:1:39:0 | init | 5 | constructors.swift:39:1:39:0 | f8 [init default constructor] | f8 | true | +defaultConstructorParameterType +| constructors.swift:39:1:39:0 | init | 2 | constructors.swift:39:1:39:0 | f2 [init default constructor] | f2 | constructors.swift:41:13:41:18 | String | +| constructors.swift:39:1:39:0 | init | 3 | constructors.swift:39:1:39:0 | f4 [init default constructor] | f4 | constructors.swift:43:13:43:18 | Double | +| constructors.swift:39:1:39:0 | init | 4 | constructors.swift:39:1:39:0 | f7 [init default constructor] | f7 | constructors.swift:53:13:53:18 | Double | +| constructors.swift:39:1:39:0 | init | 5 | constructors.swift:39:1:39:0 | f8 [init default constructor] | f8 | constructors.swift:61:13:61:16 | GenericTypeExpr | diff --git a/unified/ql/test/library-tests/constructors/test.ql b/unified/ql/test/library-tests/constructors/test.ql index ac2a2aef844f..268b69cb474a 100644 --- a/unified/ql/test/library-tests/constructors/test.ql +++ b/unified/ql/test/library-tests/constructors/test.ql @@ -11,9 +11,17 @@ query predicate implicitReceiverParameter(ParameterEx p, Callable c) { c.getFile().fromSource() } -query predicate callableExDefaultConstructorParameter( - ConstructorDeclaration cd, int i, ParameterEx p, string name +query predicate defaultConstructorParameter( + ConstructorDeclaration cd, int i, ParameterEx p, string name, boolean hasDefault ) { p.isDefaultConstructorParameter(cd, i, name) and - cd.getFile().fromSource() + cd.getFile().fromSource() and + if p.hasDefault() then hasDefault = true else hasDefault = false +} + +query predicate defaultConstructorParameterType( + ConstructorDeclaration cd, int i, ParameterEx p, string name, AstNode type +) { + defaultConstructorParameter(cd, i, p, name, _) and + type = p.getType() } diff --git a/unified/ql/test/library-tests/type-inference/constructors.swift b/unified/ql/test/library-tests/type-inference/constructors.swift index 9a370919b565..eb1d4ae49d06 100644 --- a/unified/ql/test/library-tests/type-inference/constructors.swift +++ b/unified/ql/test/library-tests/type-inference/constructors.swift @@ -8,3 +8,13 @@ class C1 { var c11 = C1() // $ type=c11:C1 target=C1.init_conv $ SPURIOUS: target=C1.init var c12 = C1.init() // $ type=c12:C1 target=C1.init_conv $ SPURIOUS: target=C1.init + +struct S1 { // implicit `init(f1: T, f2: String = "default", f3: (T) -> T = { $0 })` + var f1: T + var f2: String = "default" + var f3: (T) -> T = { $0 } +} + +var s11 = S1(f1: 0) // $ target=S1.init type=s11@S1:Int +var s12 = S1(f1: true, f2: "") // $ target=S1.init type=s12@S1:Bool +S1(f1: 0, f2: "", f3: { x in x }) // $ target=S1.init type=x:Int diff --git a/unified/ql/test/library-tests/type-inference/type-inference.expected b/unified/ql/test/library-tests/type-inference/type-inference.expected index 6b86b01d5362..ebd75b0b3ad3 100644 --- a/unified/ql/test/library-tests/type-inference/type-inference.expected +++ b/unified/ql/test/library-tests/type-inference/type-inference.expected @@ -129,6 +129,15 @@ inferCertainType | closures.swift:66:9:68:9 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | | constructors.swift:2:12:2:12 | x | | {EXTERNAL LOCATION} | Int | | constructors.swift:5:9:5:12 | self | | constructors.swift:1:1:7:1 | C1 | +| constructors.swift:13:9:13:10 | f1 | | constructors.swift:12:11:12:11 | T | +| constructors.swift:14:9:14:10 | f2 | | {EXTERNAL LOCATION} | String | +| constructors.swift:15:9:15:10 | f3 | | {EXTERNAL LOCATION} | Function | +| constructors.swift:15:9:15:10 | f3 | Args | {EXTERNAL LOCATION} | Tuple1 | +| constructors.swift:15:9:15:10 | f3 | Args.T0 | constructors.swift:12:11:12:11 | T | +| constructors.swift:15:9:15:10 | f3 | Return | constructors.swift:12:11:12:11 | T | +| constructors.swift:15:24:15:29 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| constructors.swift:20:23:20:32 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| constructors.swift:20:23:20:32 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | | fields.swift:2:7:2:20 | inheritedField | | {EXTERNAL LOCATION} | Int | | fields.swift:6:7:6:17 | storedField | | {EXTERNAL LOCATION} | String | | fields.swift:7:7:7:18 | genericField | | fields.swift:5:22:5:22 | T | @@ -1084,6 +1093,8 @@ inferType | closures.swift:69:5:69:5 | _ | | {EXTERNAL LOCATION} | String | | closures.swift:69:5:69:14 | ... = ... | | {EXTERNAL LOCATION} | String | | closures.swift:69:9:69:14 | result | | {EXTERNAL LOCATION} | String | +| constructors.swift:1:1:20:33 | Block | | constructors.swift:12:1:16:1 | S1 | +| constructors.swift:1:1:20:33 | Block | T | {EXTERNAL LOCATION} | Int | | constructors.swift:2:12:2:12 | x | | {EXTERNAL LOCATION} | Int | | constructors.swift:4:24:6:5 | Block | | constructors.swift:1:1:7:1 | C1 | | constructors.swift:5:9:5:12 | self | | constructors.swift:1:1:7:1 | C1 | @@ -1093,6 +1104,36 @@ inferType | constructors.swift:9:11:9:14 | C1(...) | | constructors.swift:1:1:7:1 | C1 | | constructors.swift:10:5:10:7 | c12 | | constructors.swift:1:1:7:1 | C1 | | constructors.swift:10:11:10:19 | ... .init(...) | | constructors.swift:1:1:7:1 | C1 | +| constructors.swift:13:9:13:10 | f1 | | constructors.swift:12:11:12:11 | T | +| constructors.swift:14:9:14:10 | f2 | | {EXTERNAL LOCATION} | String | +| constructors.swift:14:22:14:30 | "default" | | {EXTERNAL LOCATION} | String | +| constructors.swift:15:9:15:10 | f3 | | {EXTERNAL LOCATION} | Function | +| constructors.swift:15:9:15:10 | f3 | Args | {EXTERNAL LOCATION} | Tuple1 | +| constructors.swift:15:9:15:10 | f3 | Args.T0 | constructors.swift:12:11:12:11 | T | +| constructors.swift:15:9:15:10 | f3 | Return | constructors.swift:12:11:12:11 | T | +| constructors.swift:15:24:15:29 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| constructors.swift:18:5:18:7 | s11 | | constructors.swift:12:1:16:1 | S1 | +| constructors.swift:18:5:18:7 | s11 | T | {EXTERNAL LOCATION} | Int | +| constructors.swift:18:11:18:19 | S1(...) | | constructors.swift:12:1:16:1 | S1 | +| constructors.swift:18:11:18:19 | S1(...) | T | {EXTERNAL LOCATION} | Int | +| constructors.swift:18:18:18:18 | 0 | | {EXTERNAL LOCATION} | Int | +| constructors.swift:19:5:19:7 | s12 | | constructors.swift:12:1:16:1 | S1 | +| constructors.swift:19:5:19:7 | s12 | T | {EXTERNAL LOCATION} | Bool | +| constructors.swift:19:11:19:30 | S1(...) | | constructors.swift:12:1:16:1 | S1 | +| constructors.swift:19:11:19:30 | S1(...) | T | {EXTERNAL LOCATION} | Bool | +| constructors.swift:19:18:19:21 | true | | {EXTERNAL LOCATION} | Bool | +| constructors.swift:19:28:19:29 | "" | | {EXTERNAL LOCATION} | String | +| constructors.swift:20:1:20:33 | S1(...) | | constructors.swift:12:1:16:1 | S1 | +| constructors.swift:20:1:20:33 | S1(...) | T | {EXTERNAL LOCATION} | Int | +| constructors.swift:20:8:20:8 | 0 | | {EXTERNAL LOCATION} | Int | +| constructors.swift:20:15:20:16 | "" | | {EXTERNAL LOCATION} | String | +| constructors.swift:20:23:20:32 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| constructors.swift:20:23:20:32 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| constructors.swift:20:23:20:32 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Int | +| constructors.swift:20:23:20:32 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| constructors.swift:20:25:20:25 | x | | {EXTERNAL LOCATION} | Int | +| constructors.swift:20:30:20:30 | Block | | {EXTERNAL LOCATION} | Int | +| constructors.swift:20:30:20:30 | x | | {EXTERNAL LOCATION} | Int | | fields.swift:2:7:2:20 | inheritedField | | {EXTERNAL LOCATION} | Int | | fields.swift:2:29:2:29 | 0 | | {EXTERNAL LOCATION} | Int | | fields.swift:6:7:6:17 | storedField | | {EXTERNAL LOCATION} | String |