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 @@ -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
4 changes: 4 additions & 0 deletions unified/ql/lib/codeql/unified/internal/ExprPositions.qll
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ 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
isInTypeContext(expr.getEnclosingExpr())
Expand Down
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 @@ -209,6 +209,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 @@ -261,4 +267,10 @@ module Unified {
/** Gets the number of arguments passed to this call, not counting implicit arguments like receiver. */
int getNumberOfArguments() { result = count(this.getAnArgument()) }
}

/** A function expression. */
class FunctionExpr extends G::FunctionExpr {
/** Gets the number of parameters of this function. */
int getNumberOfParameters() { result = count(this.getAParameter()) }
}
}
60 changes: 41 additions & 19 deletions unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,26 @@ module Public {
LocalName getLocalName() { result = this.(LocalNameBindingOutput::LocalAccess).getLocal() }
}

final class LocalVariable = LocalVariableImpl;

/** A representative for a lexically scoped local variable. */
class LocalVariable extends LocalName {
LocalVariable() {
abstract private class LocalVariableImpl extends LocalName {
/** Gets the callable containing the declaration of this local variable. */
abstract Callable getDeclaringCallable();

/** Holds if this local variable is captured, that is, it is accessed from another callable than the one declaring it. */
predicate isCaptured() {
this.getAnAccess().getEnclosingCallable() != this.getDeclaringCallable()
}

/**
* Holds if this local variable represents an implicit receiver parameter of the given callable.
*/
abstract predicate isImplicitReceiverParameter(Callable c);
}

private class ExplicitLocalVariable extends LocalVariableImpl {
ExplicitLocalVariable() {
exists(AstNode decl |
decl = this.getABinding().getDeclaration() and
not isInstanceMember(decl) and
Expand All @@ -411,29 +428,34 @@ module Public {
decl instanceof CatchClause or
decl instanceof SwitchCase
)
or
}

override Callable getDeclaringCallable() { result = this.getABinding().getEnclosingCallable() }

override predicate isImplicitReceiverParameter(Callable c) { none() }
}

private class ImplicitLocalVariable extends LocalVariableImpl instanceof LocalNameBindingOutput::ImplicitLocal
{
AstNode scope;
string name;

ImplicitLocalVariable() {
// For implicitly-declared locals we can't expect to find a binding. Check 'implicitDeclInScope' directly.
exists(AstNode scope, string name |
this.(LocalNameBindingOutput::ImplicitLocal).hasNameAndScope(name, scope) and
LocalNameBindingInput::implicitDeclInScope(name, scope, true)
)
super.hasNameAndScope(name, scope) and
LocalNameBindingInput::implicitDeclInScope(name, scope, true)
}

/** Gets the callable containing the declaration of this local variable. */
Callable getDeclaringCallable() {
result = this.getABinding().getEnclosingCallable()
override Callable getDeclaringCallable() {
result = scope
or
exists(AstNode scope | scope = this.(LocalNameBindingOutput::ImplicitLocal).getScope() |
result = scope
or
not scope instanceof Callable and
result = scope.getEnclosingCallable()
)
not scope instanceof Callable and
result = scope.getEnclosingCallable()
}

/** Holds if this local variable is captured, that is, it is accessed from another callable than the one declaring it. */
predicate isCaptured() {
this.getAnAccess().getEnclosingCallable() != this.getDeclaringCallable()
override predicate isImplicitReceiverParameter(Callable c) {
name = any(NameBindingPlugin p).getImplicitReceiverParameterName(scope) and
scope = c
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ class NameBindingNode extends TNameBindingNode {
Identifier getIdentifierFromRef(AstNode n) {
result = n.(PotentialLocalNameAccess)
or
result = getIdentifierFromRef(n.(GenericTypeExpr).getBase())
or
result = n.(MemberAccessExpr).getMemberNameNode()
}

Expand Down
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
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 }
}
Loading
Loading