mirror of
https://github.com/github/codeql.git
synced 2026-04-23 07:45:17 +02:00
Java: Refactor definitions query, add queries for ide search
This enables jump-to-definition and find-references in the VS Code extension, for Java source archives.
This commit is contained in:
@@ -2,3 +2,8 @@
|
||||
- qlpack: codeql-java
|
||||
- apply: lgtm-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
# These are only for IDE use.
|
||||
- exclude:
|
||||
tags contain:
|
||||
- ide-contextual-queries/local-definitions
|
||||
- ide-contextual-queries/local-references
|
||||
@@ -6,194 +6,8 @@
|
||||
* @id java/jump-to-definition
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* Restricts the location of a method access to the method identifier only,
|
||||
* excluding its qualifier, type arguments and arguments.
|
||||
*
|
||||
* If there is any whitespace between the method identifier and its first argument,
|
||||
* or between the method identifier and its qualifier (or last type argument, if any),
|
||||
* the location may be slightly inaccurate and include such whitespace,
|
||||
* but it should suffice for the purpose of avoiding overlapping definitions.
|
||||
*/
|
||||
class LocationOverridingMethodAccess extends MethodAccess {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(MemberRefExpr e | e.getReferencedCallable() = getMethod() |
|
||||
exists(int elRef, int ecRef | e.hasLocationInfo(path, _, _, elRef, ecRef) |
|
||||
sl = elRef and
|
||||
sc = ecRef - getMethod().getName().length() + 1 and
|
||||
el = elRef and
|
||||
ec = ecRef
|
||||
)
|
||||
)
|
||||
or
|
||||
not exists(MemberRefExpr e | e.getReferencedCallable() = getMethod()) and
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
(
|
||||
if exists(getTypeArgument(_))
|
||||
then
|
||||
exists(Location locTypeArg |
|
||||
locTypeArg = getTypeArgument(count(getTypeArgument(_)) - 1).getLocation()
|
||||
|
|
||||
sl = locTypeArg.getEndLine() and
|
||||
sc = locTypeArg.getEndColumn() + 2
|
||||
)
|
||||
else (
|
||||
if exists(getQualifier())
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the qualifier, not the modified one.
|
||||
exists(Location locQual | locQual = getQualifier().getLocation() |
|
||||
sl = locQual.getEndLine() and
|
||||
sc = locQual.getEndColumn() + 2
|
||||
)
|
||||
else (
|
||||
sl = slSuper and
|
||||
sc = scSuper
|
||||
)
|
||||
)
|
||||
) and
|
||||
(
|
||||
if getNumArgument() > 0
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the first argument, not the modified one.
|
||||
exists(Location locArg | locArg = getArgument(0).getLocation() |
|
||||
el = locArg.getStartLine() and
|
||||
ec = locArg.getStartColumn() - 2
|
||||
)
|
||||
else (
|
||||
el = elSuper and
|
||||
ec = ecSuper - 2
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a type access to exclude
|
||||
* the type arguments and qualifier, if any.
|
||||
*/
|
||||
class LocationOverridingTypeAccess extends TypeAccess {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
(
|
||||
if exists(getQualifier())
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the qualifier, not the modified one.
|
||||
exists(Location locQual | locQual = getQualifier().getLocation() |
|
||||
sl = locQual.getEndLine() and
|
||||
sc = locQual.getEndColumn() + 2
|
||||
)
|
||||
else (
|
||||
sl = slSuper and
|
||||
sc = scSuper
|
||||
)
|
||||
) and
|
||||
(
|
||||
if exists(getTypeArgument(_))
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the first type argument, not the modified one.
|
||||
exists(Location locArg | locArg = getTypeArgument(0).getLocation() |
|
||||
el = locArg.getStartLine() and
|
||||
ec = locArg.getStartColumn() - 2
|
||||
)
|
||||
else (
|
||||
el = elSuper and
|
||||
ec = ecSuper
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a field access to the name of the accessed field only,
|
||||
* excluding its qualifier.
|
||||
*/
|
||||
class LocationOverridingFieldAccess extends FieldAccess {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
super.hasLocationInfo(path, _, _, el, ec) and
|
||||
sl = el and
|
||||
sc = ec - getField().getName().length() + 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a single-type-import declaration to the name of the imported type only,
|
||||
* excluding the `import` keyword and the package name.
|
||||
*/
|
||||
class LocationOverridingImportType extends ImportType {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
el = elSuper and
|
||||
ec = ecSuper - 1 and
|
||||
sl = el and
|
||||
sc = ecSuper - getImportedType().getName().length()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a single-static-import declaration to the name of the imported member(s) only,
|
||||
* excluding the `import` keyword and the package name.
|
||||
*/
|
||||
class LocationOverridingImportStaticTypeMember extends ImportStaticTypeMember {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
el = elSuper and
|
||||
ec = ecSuper - 1 and
|
||||
sl = el and
|
||||
sc = ecSuper - getName().length()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Element definition(Element e, string kind) {
|
||||
e.(MethodAccess).getMethod().getSourceDeclaration() = result and
|
||||
kind = "M" and
|
||||
not result instanceof InitializerMethod
|
||||
or
|
||||
e.(TypeAccess).getType().(RefType).getSourceDeclaration() = result and kind = "T"
|
||||
or
|
||||
exists(Variable v | v = e.(VarAccess).getVariable() |
|
||||
result = v.(Field).getSourceDeclaration() or
|
||||
result = v.(Parameter).getSourceDeclaration() or
|
||||
result = v.(LocalVariableDecl)
|
||||
) and
|
||||
kind = "V"
|
||||
or
|
||||
e.(ImportType).getImportedType() = result and kind = "I"
|
||||
or
|
||||
e.(ImportStaticTypeMember).getAMemberImport() = result and kind = "I"
|
||||
}
|
||||
|
||||
predicate dummyVarAccess(VarAccess va) {
|
||||
exists(AssignExpr ae, InitializerMethod im |
|
||||
ae.getDest() = va and
|
||||
ae.getParent() = im.getBody().getAChild()
|
||||
)
|
||||
}
|
||||
|
||||
predicate dummyTypeAccess(TypeAccess ta) {
|
||||
exists(FunctionalExpr e |
|
||||
e.getAnonymousClass().getClassInstanceExpr().getTypeName() = ta.getParent*()
|
||||
)
|
||||
}
|
||||
import definitions
|
||||
|
||||
from Element e, Element def, string kind
|
||||
where
|
||||
def = definition(e, kind) and
|
||||
def.fromSource() and
|
||||
e.fromSource() and
|
||||
not dummyVarAccess(e) and
|
||||
not dummyTypeAccess(e)
|
||||
where def = definitionOf(e, kind)
|
||||
select e, def, kind
|
||||
|
||||
212
java/ql/src/definitions.qll
Normal file
212
java/ql/src/definitions.qll
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Provides classes and predicates related to jump-to-definition links
|
||||
* in the code viewer.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* Restricts the location of a method access to the method identifier only,
|
||||
* excluding its qualifier, type arguments and arguments.
|
||||
*
|
||||
* If there is any whitespace between the method identifier and its first argument,
|
||||
* or between the method identifier and its qualifier (or last type argument, if any),
|
||||
* the location may be slightly inaccurate and include such whitespace,
|
||||
* but it should suffice for the purpose of avoiding overlapping definitions.
|
||||
*/
|
||||
class LocationOverridingMethodAccess extends MethodAccess {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(MemberRefExpr e | e.getReferencedCallable() = getMethod() |
|
||||
exists(int elRef, int ecRef | e.hasLocationInfo(path, _, _, elRef, ecRef) |
|
||||
sl = elRef and
|
||||
sc = ecRef - getMethod().getName().length() + 1 and
|
||||
el = elRef and
|
||||
ec = ecRef
|
||||
)
|
||||
)
|
||||
or
|
||||
not exists(MemberRefExpr e | e.getReferencedCallable() = getMethod()) and
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
(
|
||||
if exists(getTypeArgument(_))
|
||||
then
|
||||
exists(Location locTypeArg |
|
||||
locTypeArg = getTypeArgument(count(getTypeArgument(_)) - 1).getLocation()
|
||||
|
|
||||
sl = locTypeArg.getEndLine() and
|
||||
sc = locTypeArg.getEndColumn() + 2
|
||||
)
|
||||
else (
|
||||
if exists(getQualifier())
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the qualifier, not the modified one.
|
||||
exists(Location locQual | locQual = getQualifier().getLocation() |
|
||||
sl = locQual.getEndLine() and
|
||||
sc = locQual.getEndColumn() + 2
|
||||
)
|
||||
else (
|
||||
sl = slSuper and
|
||||
sc = scSuper
|
||||
)
|
||||
)
|
||||
) and
|
||||
(
|
||||
if getNumArgument() > 0
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the first argument, not the modified one.
|
||||
exists(Location locArg | locArg = getArgument(0).getLocation() |
|
||||
el = locArg.getStartLine() and
|
||||
ec = locArg.getStartColumn() - 2
|
||||
)
|
||||
else (
|
||||
el = elSuper and
|
||||
ec = ecSuper - 2
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a type access to exclude
|
||||
* the type arguments and qualifier, if any.
|
||||
*/
|
||||
class LocationOverridingTypeAccess extends TypeAccess {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
(
|
||||
if exists(getQualifier())
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the qualifier, not the modified one.
|
||||
exists(Location locQual | locQual = getQualifier().getLocation() |
|
||||
sl = locQual.getEndLine() and
|
||||
sc = locQual.getEndColumn() + 2
|
||||
)
|
||||
else (
|
||||
sl = slSuper and
|
||||
sc = scSuper
|
||||
)
|
||||
) and
|
||||
(
|
||||
if exists(getTypeArgument(_))
|
||||
then
|
||||
// Note: this needs to be the original (full) location of the first type argument, not the modified one.
|
||||
exists(Location locArg | locArg = getTypeArgument(0).getLocation() |
|
||||
el = locArg.getStartLine() and
|
||||
ec = locArg.getStartColumn() - 2
|
||||
)
|
||||
else (
|
||||
el = elSuper and
|
||||
ec = ecSuper
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a field access to the name of the accessed field only,
|
||||
* excluding its qualifier.
|
||||
*/
|
||||
class LocationOverridingFieldAccess extends FieldAccess {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
super.hasLocationInfo(path, _, _, el, ec) and
|
||||
sl = el and
|
||||
sc = ec - getField().getName().length() + 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a single-type-import declaration to the name of the imported type only,
|
||||
* excluding the `import` keyword and the package name.
|
||||
*/
|
||||
class LocationOverridingImportType extends ImportType {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
el = elSuper and
|
||||
ec = ecSuper - 1 and
|
||||
sl = el and
|
||||
sc = ecSuper - getImportedType().getName().length()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts the location of a single-static-import declaration to the name of the imported member(s) only,
|
||||
* excluding the `import` keyword and the package name.
|
||||
*/
|
||||
class LocationOverridingImportStaticTypeMember extends ImportStaticTypeMember {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int slSuper, int scSuper, int elSuper, int ecSuper |
|
||||
super.hasLocationInfo(path, slSuper, scSuper, elSuper, ecSuper)
|
||||
|
|
||||
el = elSuper and
|
||||
ec = ecSuper - 1 and
|
||||
sl = el and
|
||||
sc = ecSuper - getName().length()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Element definition(Element e, string kind) {
|
||||
e.(MethodAccess).getMethod().getSourceDeclaration() = result and
|
||||
kind = "M" and
|
||||
not result instanceof InitializerMethod
|
||||
or
|
||||
e.(TypeAccess).getType().(RefType).getSourceDeclaration() = result and kind = "T"
|
||||
or
|
||||
exists(Variable v | v = e.(VarAccess).getVariable() |
|
||||
result = v.(Field).getSourceDeclaration() or
|
||||
result = v.(Parameter).getSourceDeclaration() or
|
||||
result = v.(LocalVariableDecl)
|
||||
) and
|
||||
kind = "V"
|
||||
or
|
||||
e.(ImportType).getImportedType() = result and kind = "I"
|
||||
or
|
||||
e.(ImportStaticTypeMember).getAMemberImport() = result and kind = "I"
|
||||
}
|
||||
|
||||
predicate dummyVarAccess(VarAccess va) {
|
||||
exists(AssignExpr ae, InitializerMethod im |
|
||||
ae.getDest() = va and
|
||||
ae.getParent() = im.getBody().getAChild()
|
||||
)
|
||||
}
|
||||
|
||||
predicate dummyTypeAccess(TypeAccess ta) {
|
||||
exists(FunctionalExpr e |
|
||||
e.getAnonymousClass().getClassInstanceExpr().getTypeName() = ta.getParent*()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an element, of kind `kind`, that element `e` uses, if any.
|
||||
*
|
||||
* The `kind` is a string representing what kind of use it is:
|
||||
* - `"M"` for function and method calls
|
||||
* - `"T"` for uses of types
|
||||
* - `"V"` for variable accesses
|
||||
* - `"I"` for import directives
|
||||
*/
|
||||
Element definitionOf(Element e, string kind) {
|
||||
result = definition(e, kind) and
|
||||
result.fromSource() and
|
||||
e.fromSource() and
|
||||
not dummyVarAccess(e) and
|
||||
not dummyTypeAccess(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriately encoded version of a filename `name`
|
||||
* passed by the VS Code extension in order to coincide with the
|
||||
* output of `.getFile()` on locatable entities.
|
||||
*/
|
||||
cached
|
||||
File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name }
|
||||
16
java/ql/src/localDefinitions.ql
Normal file
16
java/ql/src/localDefinitions.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Jump-to-definition links
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for jump-to-definition in the code viewer.
|
||||
* @kind definitions
|
||||
* @id java/ide-jump-to-definition
|
||||
* @tags ide-contextual-queries/local-definitions
|
||||
*/
|
||||
|
||||
import definitions
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
from Element e, Element def, string kind
|
||||
where def = definitionOf(e, kind) and e.getFile() = getEncodedFile(selectedSourceFile())
|
||||
select e, def, kind
|
||||
16
java/ql/src/localReferences.ql
Normal file
16
java/ql/src/localReferences.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Find-references links
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for find-references in the code viewer.
|
||||
* @kind definitions
|
||||
* @id java/ide-find-references
|
||||
* @tags ide-contextual-queries/local-references
|
||||
*/
|
||||
|
||||
import definitions
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
from Element e, Element def, string kind
|
||||
where def = definitionOf(e, kind) and def.getFile() = getEncodedFile(selectedSourceFile())
|
||||
select e, def, kind
|
||||
Reference in New Issue
Block a user