Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2579,7 +2579,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
or
exists(Parameter p, Variable v |
implicitParameterDecl(p, v) and
result = p.getType().getTypeAt(path) and
tm = p.getType() and
n = v.getAnAccess()
)
)
Expand All @@ -2606,6 +2606,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
or
exists(VariableDeclaration decl |
decl.preservesInitializerType() and
not exists(decl.getType()) and
n1 = decl.getInitializer() and
n2 = decl.getPattern()
)
Expand Down
8 changes: 8 additions & 0 deletions unified/ql/consistency-queries/TypeInferenceConsistency.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @name Type inference inconsistencies
* @description Lists the type inference inconsistencies in the database. This query is intended for internal use.
* @kind table
* @id unified/diagnostics/type-inference-consistency
*/

import codeql.unified.internal.typeinference.TypeInferenceConsistency
17 changes: 16 additions & 1 deletion unified/ql/lib/codeql/unified/internal/ExprPositions.qll
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
private import unified
private import NameBinding as NameBinding
private import ExprPositionsPlugin

/**
* Holds if `expr` appears in the context of a type annotation.
* Holds if `expr` appears in a context where it must refer to a type.
*/
predicate isInTypeContext(Expr expr) {
expr = any(TypeCastExpr n).getType()
Expand All @@ -27,8 +28,22 @@ predicate isInTypeContext(Expr expr) {
or
expr = any(AssociatedTypeDeclaration n).getBound()
or
expr = any(ClassLikeDeclaration c).getExtensionTarget()
or
expr = any(GenericTypeExpr gte).getATypeArgument()
or
expr.getParent() instanceof TypeConstraint
or
exists(Identifier id | id = NameBinding::getStaticBindingTarget(expr) |
id = any(ClassLikeDeclaration c).getNameNode()
or
id = any(TypeAliasDeclaration t).getNameNode()
or
id = any(TypeParameter t).getNameNode()
)
or
any(ExprPositionsPlugin p).isInTypeContext(expr)
or
isInTypeContext(expr.getEnclosingExpr())
}

Expand Down
10 changes: 10 additions & 0 deletions unified/ql/lib/codeql/unified/internal/ExprPositionsPlugin.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
private import unified
private import codeql.util.Unit

private module Plugins {
private import ExprPositionsPluginSwift
}

class ExprPositionsPlugin extends Unit {
predicate isInTypeContext(Expr e) { none() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
private import unified
private import ExprPositionsPlugin

private class ExprPositionsPluginSwift extends ExprPositionsPlugin {
override predicate isInTypeContext(Expr e) { e = any(GenericTypeExpr g).getBase() }
}
12 changes: 12 additions & 0 deletions unified/ql/lib/codeql/unified/internal/FacadeAst.qll
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ module Unified {
}
}

/** A tuple expression. */
class TupleExpr extends G::TupleExpr {
/** Gets the number of elements in this tuple expression. */
int getNumberOfElements() { result = count(this.getAnElement()) }
}

class TypeAliasDeclaration extends G::TypeAliasDeclaration {
/** Gets the name of this type alias. */
string getName() { result = this.getNameNode().getValue() }
Expand Down Expand Up @@ -274,4 +280,10 @@ module Unified {
result = count(Argument arg | arg = this.getAnArgument() and arg.isPositional())
}
}

/** A function expression. */
class FunctionExpr extends G::FunctionExpr {
/** Gets the number of parameters of this function. */
int getNumberOfParameters() { result = count(this.getAParameter()) }
}
}
13 changes: 2 additions & 11 deletions unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
private import unified
private import AllDataFlow
private import codeql.unified.internal.NameBinding as N

private Callable getCallableFromNameBinding(NameBinding binding) {
binding = result.(FunctionDeclaration).getNameNode()
}
private import codeql.unified.internal.typeinference.TypeInference as T

DataFlowCallable viableCallable(DataFlowCall c) {
exists(CallExpr call, Callable callable, NameBinding target |
c.asExplicitCall() = call and
target = N::getStaticBindingTarget(N::getIdentifierFromRef(call.getCallee())) and
callable = getCallableFromNameBinding(target) and
result.asSourceCallable() = callable
)
result.asSourceCallable() = T::resolveCallTarget(c.asExplicitCall())
}
219 changes: 219 additions & 0 deletions unified/ql/lib/codeql/unified/internal/typeinference/Type.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/** Provides classes representing types without type arguments. */

import unified // reexport in order to shadow e.g. the `TypeParameter` class
private import unified as Unified
private import TypeInference
private import codeql.unified.internal.StaticNameBinding

/**
* Holds if `a` is an associated type of `c`, in which case we model it
* as a type parameter.
*
* `inherited` indicates whether the associated type is declared in `c`
* or inherited from a base class.
*/
private predicate associatedTypeParameter(
ClassLikeDeclaration c, AssociatedTypeDeclaration a, boolean inherited
) {
a = c.getAMember() and
inherited = false
or
exists(string name |
associatedTypeParameterInherited(c, a, _, _, name) and
not c.getAMember().(TypeAliasDeclaration).getName() = name and
inherited = true
)
}

pragma[nomagic]
predicate associatedTypeParameterInherited(
ClassLikeDeclaration c, AssociatedTypeDeclaration a, ClassLikeDeclaration base, Expr baseRef,
string name
) {
associatedTypeParameter(base, a, _) and
baseRef = c.getABaseType().getType() and
base.getNameNode() = getStaticBindingTarget(baseRef) and
name = a.getName()
}

cached
newtype TType =
TClassLikeDeclarationType(ClassLikeDeclaration c) {
CachedStage::ref() and
exists(c.getNameNode())
} or
TClosureParameterPseudoType(Parameter p) {
exists(FunctionExpr fe |
p = fe.getAParameter() and
not exists(p.getType())
)
} or
TTypeParameterType(Unified::TypeParameter tp) or
TAssociatedTypeParameterType(
ClassLikeDeclaration c, AssociatedTypeDeclaration a, boolean inherited
) {
associatedTypeParameter(c, a, inherited)
} or
TUnknownType() or
TUnknownTypeTypeParameter(int i) { i in [0 .. 20] }

final class Type = TypeImpl;

/**
* A type without type arguments.
*/
abstract private class TypeImpl extends TType {
/**
* Gets the `i`th positional type parameter of this type, if any.
*
* This excludes synthetic type parameters, such as associated types.
*/
abstract TypeParameter getPositionalTypeParameter(int i);

/**
* Gets a type parameter of this type.
*
* This includes both positional type parameters and synthetic type parameters,
* such as associated types.
*/
TypeParameter getATypeParameter() { result = this.getPositionalTypeParameter(_) }

/** Gets a textual representation of this type. */
abstract string toString();

/** Gets the location of this type. */
abstract Location getLocation();
}

/**
* A type representing a class-like declaration.
*/
class ClassLikeDeclarationType extends TypeImpl, TClassLikeDeclarationType {
ClassLikeDeclaration c;

ClassLikeDeclarationType() { this = TClassLikeDeclarationType(c) }

ClassLikeDeclaration getClassLikeDeclaration() { result = c }

string getName() { result = c.getName() }

override TypeParameter getPositionalTypeParameter(int i) {
result = TTypeParameterType(c.getTypeParameter(i))
}

override TypeParameter getATypeParameter() {
result = super.getATypeParameter()
or
result = TAssociatedTypeParameterType(c, _, _)
}

override string toString() { result = c.getName() }

override Location getLocation() { result = c.getLocation() }
}

/**
* A pseudo type that does not correspond to any concrete type in the source code.
*/
abstract class PseudoType extends TypeImpl { }

/**
* A type representing an unknown type.
*/
class UnknownType extends PseudoType, TUnknownType {
override TypeParameter getPositionalTypeParameter(int i) { result = TUnknownTypeTypeParameter(i) }

override string toString() { result = "(unknown type)" }

override Location getLocation() { result instanceof EmptyLocation }
}

/**
* A type representing a closure parameter.
*/
class ClosureParameterPseudoType extends PseudoType, TClosureParameterPseudoType {
private Parameter param;

ClosureParameterPseudoType() { this = TClosureParameterPseudoType(param) }

Parameter getParam() { result = param }

override TypeParameter getPositionalTypeParameter(int i) { none() }

override string toString() { result = "(closure parameter " + param + ")" }

override Location getLocation() { result = param.getLocation() }
}

/** A type parameter. */
abstract class TypeParameter extends TypeImpl {
override TypeParameter getPositionalTypeParameter(int i) { none() }

abstract AstNode getDeclaringItem();
}

private class IdAstNode =
@unified_type_parameter or @unified_class_like_declaration or @unified_associated_type_declaration;

private predicate id(IdAstNode x, IdAstNode y) { x = y }

private predicate idOf(IdAstNode x, int y) = equivalenceRelation(id/2)(x, y)

int idOfTypeParameterAstNode(AstNode node) { idOf(node, result) }

/** A type parameter from source code. */
class TypeParameterType extends TypeParameter, TTypeParameterType {
private Unified::TypeParameter typeParam;

TypeParameterType() { this = TTypeParameterType(typeParam) }

Unified::TypeParameter getTypeParameter() { result = typeParam }

override ClassLikeDeclaration getDeclaringItem() { typeParam = result.getATypeParameter() }

override string toString() { result = typeParam.getName() }

override Location getLocation() { result = typeParam.getLocation() }
}

/**
* An associated type viewed as a type parameter.
*/
class AssociatedTypeParameterType extends TypeParameter, TAssociatedTypeParameterType {
private Unified::ClassLikeDeclaration c;
private Unified::AssociatedTypeDeclaration assocTypeDecl;
private boolean inherited;

AssociatedTypeParameterType() { this = TAssociatedTypeParameterType(c, assocTypeDecl, inherited) }

Unified::AssociatedTypeDeclaration getAssociatedTypeDeclaration() { result = assocTypeDecl }

override ClassLikeDeclaration getDeclaringItem() { result = c }

override string toString() {
if inherited = true
then result = assocTypeDecl.getName() + " (inherited)"
else result = assocTypeDecl.getName()
}

override Location getLocation() {
if inherited = true then result = c.getLocation() else result = assocTypeDecl.getLocation()
}
}

/**
* A type parameter of the special `UnknownType`.
*/
class UnknownTypeTypeParameter extends TypeParameter, TUnknownTypeTypeParameter {
private int i;

UnknownTypeTypeParameter() { this = TUnknownTypeTypeParameter(i) }

override AstNode getDeclaringItem() { none() }

override TypeParameter getPositionalTypeParameter(int j) { none() }

override string toString() { result = "unknown type parameter " + i }

override Location getLocation() { result instanceof EmptyLocation }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
private import unified as Unified
private import Type

/**
* A type abstraction. I.e., a place in the program where type variables may
* be introduced.
*/
abstract class TypeAbstraction extends AstNode {
abstract TypeParameter getATypeParameter();
}

/**
* A class-like declaration. Unlike `ClassLikeDeclarationType`, this includes
* declarations that do not define a new type, such as `extension`s in Swift.
*/
private class ClassLikeDeclarationTypeAbstraction extends TypeAbstraction instanceof Unified::ClassLikeDeclaration
{
override TypeParameter getATypeParameter() { this = result.getDeclaringItem() }
}
Loading
Loading