Files
codeql/csharp/ql/lib/definitions.qll
Andrew Eisenberg a3f4d1bf66 Move contextual queries from src to lib
With this change, users are now able to run View AST command in
vscode within vscode workspaces that do not include the core libraries.
The relevant core library only needs to be installed in the package
cache.
2022-06-29 07:51:26 -07:00

199 lines
6.4 KiB
Plaintext

/**
* Provides classes and predicates related to jump-to-definition links
* in the code viewer.
*/
import csharp
import IDEContextual
/** An element with an associated definition. */
abstract class Use extends @type_mention_parent {
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(Location l |
l = this.(Element).getLocation() or
l = this.(TypeMention).getLocation()
|
filepath = l.getFile().getAbsolutePath() and
startline = l.getStartLine() and
startcolumn = l.getStartColumn() and
endline = l.getEndLine() and
endcolumn = l.getEndColumn()
)
}
/** Gets the definition associated with this element. */
abstract Declaration getDefinition();
/**
* Gets the type of use.
*
* - `"M"`: call.
* - `"V"`: variable use.
* - `"T"`: type reference.
*/
abstract string getUseType();
/** Gets a textual representation of this element. */
abstract string toString();
}
/** A method call/access. */
private class MethodUse extends Use, QualifiableExpr {
MethodUse() {
this instanceof MethodCall or
this instanceof MethodAccess
}
/** Gets the qualifier of this method use, if any. */
private Expr getFormatQualifier() {
(
if this.getQualifiedDeclaration().(Method).isExtensionMethod()
then result = this.(MethodCall).getArgument(0)
else result = this.getQualifier()
) and
not result.isImplicit()
}
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
Use.super.hasLocationInfo(filepath, _, _, _, _) and
endline = startline and
endcolumn = startcolumn + this.getQualifiedDeclaration().getName().length() - 1 and
(
exists(Location ql | ql = this.getFormatQualifier().getLocation() |
startline = ql.getEndLine() and
startcolumn = ql.getEndColumn() + 2
)
or
not exists(this.getFormatQualifier()) and
exists(Location l | l = this.getLocation() |
startline = l.getStartLine() and
startcolumn = l.getStartColumn()
)
)
}
override Method getDefinition() {
result = this.getQualifiedDeclaration().getUnboundDeclaration()
}
override string getUseType() { result = "M" }
override string toString() { result = this.(Expr).toString() }
}
/** An access. */
private class AccessUse extends Access, Use {
AccessUse() {
not this.getTarget().(Parameter).getCallable() instanceof Accessor and
not this = any(LocalVariableDeclAndInitExpr d).getLValue() and
not this.isImplicit() and
not this instanceof MethodAccess and // handled by `MethodUse`
not this instanceof TypeAccess and // handled by `TypeMentionUse`
not this.(FieldAccess).getParent() instanceof Field and // Enum initializer
not this.(FieldAccess).getParent().getParent() instanceof Field and // Field initializer
not this.(PropertyAccess).getParent().getParent() instanceof Property // Property initializer
}
/** Gets the qualifier of this access, if any. */
private Expr getFormatQualifier() {
result = this.(QualifiableExpr).getQualifier() and
not result.isImplicit()
}
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(Location ql | ql = this.getFormatQualifier().getLocation() |
startline = ql.getEndLine() and
startcolumn = ql.getEndColumn() + 2 and
Use.super.hasLocationInfo(filepath, _, _, endline, endcolumn)
)
or
not exists(this.getFormatQualifier()) and
Use.super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override Declaration getDefinition() { result = this.getTarget().getUnboundDeclaration() }
override string getUseType() {
if this instanceof Call or this instanceof LocalFunctionAccess
then result = "M"
else
if this instanceof BaseAccess or this instanceof ThisAccess
then result = "T"
else result = "V"
}
override string toString() { result = this.(Access).toString() }
}
/** A type mention. */
private class TypeMentionUse extends Use, TypeMention {
TypeMentionUse() {
// In type mentions such as `T[]`, `T?`, `T*`, and `(S, T)`, we only want
// uses for the nested type mentions
forall(TypeMention child, Type t |
child.getParent() = this and
t = this.getType()
|
not t instanceof ArrayType and
not t instanceof NullableType and
not t instanceof PointerType and
not t instanceof TupleType
)
}
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
Use.super.hasLocationInfo(filepath, startline, startcolumn, endline, _) and
endcolumn =
startcolumn +
this.getType().(ConstructedType).getUnboundGeneric().getUndecoratedName().length() - 1
or
Use.super.hasLocationInfo(filepath, startline, startcolumn, endline, _) and
endcolumn = startcolumn + this.getType().(UnboundGenericType).getUndecoratedName().length() - 1
or
not this.getType() instanceof ConstructedType and
not this.getType() instanceof UnboundGenericType and
Use.super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override Type getDefinition() { result = this.getType().getUnboundDeclaration() }
override string getUseType() {
if this.getTarget() instanceof ObjectCreation
then result = "M" // constructor call
else result = "T"
}
override string toString() { result = TypeMention.super.toString() }
}
/**
* Gets an element, of kind `kind`, that element `e` uses, if any.
*/
cached
Declaration definitionOf(Use use, string kind) {
result = use.getDefinition() and
result.fromSource() and
kind = use.getUseType() and
// Some entities have many locations. This can arise for files that
// are duplicated multiple times in the database at different
// locations. Rather than letting the result set explode, we just
// exclude results that are "too ambiguous" -- we could also arbitrarily
// pick one location later on.
strictcount(result.getLocation()) < 10
}