Skip to content

Java: Add Callable.getErasureStringSignature() #8761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
75 changes: 54 additions & 21 deletions java/ql/lib/semmle/code/java/Member.qll
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,16 @@ class Callable extends StmtParent, Member, @callable {
* The format of the string is `<name><params>`, where `<name>` is the result of
* the predicate `getName()` and `<params>` is the result of `paramsString()`.
* For example, the method `void printf(java.lang.String, java.lang.Object...)`
* has the string signature `printf(String, Object[])`.
* has the string signature `printf(String, Object[])`. For parameters of
* parameterized type all their type arguments are included, for example,
* `Function<? super String,? extends Integer>` (note that here a comma without
* space separates the arguments).
*
* Use `getSignature` to obtain a signature including fully qualified type names.
* Use `getSignature()` to obtain a signature including fully qualified type names.
* However, that predicate uses the erasure of the parameter types.
*
* To check if a callable has a specific signature, it might be easier to
* use the predicate `getErasureStringSignature()`.
*/
string getStringSignature() { result = this.getName() + this.paramsString() }

Expand All @@ -201,34 +208,60 @@ class Callable extends StmtParent, Member, @callable {
* representation is used. If this callable has no parameters, the result is `()`.
*
* For example, the method `void printf(java.lang.String, java.lang.Object...)`
* has the params string `(String, Object[])`.
* has the params string `(String, Object[])`. For parameters of parameterized type
* all their type arguments are included, for example,
* `Function<? super String,? extends Integer>` (note that here a comma without space
* separates the arguments).
*/
pragma[nomagic]
string paramsString() {
exists(int n | n = this.getNumberOfParameters() |
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have rewritten this to use concat. The public Git history did not indicate why this was previously implemented this way. So if there are any internal reasons for this, I can revert the change if you want.

n = 0 and result = "()"
or
n > 0 and result = "(" + this.paramUpTo(n - 1) + ")"
)
result =
"(" +
concat(Type paramType, int index |
paramType = this.getParameterType(index)
|
paramType.toString(), ", " order by index
) + ")"
}

/**
* Gets a string containing the parameter types of this callable
* from left to right, up to (and including) the `n`-th parameter.
* Holds if this callable has the specified string signature.
*
* This predicate simply tests if `sig` is equal to the result of the
* `getStringSignature()` predicate.
*
* Note: When checking for the signature of a callable, is it usually easier to
* use the predicate `hasErasureStringSignature(string)` instead.
*/
private string paramUpTo(int n) {
n = 0 and result = this.getParameterType(0).toString()
or
n > 0 and result = this.paramUpTo(n - 1) + ", " + this.getParameterType(n)
predicate hasStringSignature(string sig) { sig = this.getStringSignature() }

/**
* Gets the signature of this callable, including its name and the erasure of the
* types of all its parameters, identified by their simple (unqualified) names.
*
* The format of the string is `<name>(<params>)`, where `<name>` is the result of
* the predicate `getName()` and `<params>` are the simple names of the erasure
* of the parameter types, joined by a comma and a space.
*
* For example, the method `void addTo(java.util.List<java.lang.String>, java.lang.String...)`
* has the erasure string signature `addTo(List, String[])`.
*/
string getErasureStringSignature() {
result =
this.getName() + "(" +
concat(Type paramType, int index |
paramType = this.getParameterType(index)
|
paramType.getErasure().getName(), ", " order by index
) + ")"
}

/**
* Holds if this callable has the specified string signature.
* Holds if this callable has the specified erasure string signature.
*
* This predicate simply tests if `sig` is equal to the result of the
* `getStringSignature()` predicate.
* `getErasureStringSignature()` predicate.
*/
predicate hasStringSignature(string sig) { sig = this.getStringSignature() }
predicate hasErasureStringSignature(string sig) { sig = this.getErasureStringSignature() }

/** Gets an exception that occurs in the `throws` clause of this callable. */
Exception getAnException() { exceptions(result, _, this) }
Expand Down Expand Up @@ -266,9 +299,9 @@ class Callable extends StmtParent, Member, @callable {
predicate isVarargs() { this.getAParameter().isVarargs() }

/**
* Gets the signature of this callable, where all types in the signature have a fully-qualified name.
* The parameter types are only separated by a comma (without space). If this callable has
* no parameters, the callable name is followed by `()`.
* Gets the signature of this callable, that is, the name followed by the fully-qualified names
* of the erasure of all parameter types. The types are separated by a comma (without space).
* If this callable has no parameters, the callable name is followed by `()`.
*
* For example, method `void m(String s, int i)` has the signature `m(java.lang.String,int)`.
*/
Expand Down
11 changes: 10 additions & 1 deletion java/ql/test/library-tests/method-signatures/Test.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import java.lang.annotation.*;
import java.util.function.Function;

public class Test {

Expand All @@ -9,5 +10,13 @@ class Inner { }

public void annotations(@NotNull byte[] b1, byte @NotNull [] b2, @NotNull String s, Class<@NotNull String> c, @NotNull Test.Inner ti, Class<? extends @NotNull String> wc, Class<String>[] classes, @NotNull byte b, @NotNull String[] sArray, String @NotNull [] sArray2) { }

}
public <T, R> R typeVariables(Function<? super T, ? extends R> f) {
return null;
}

@SafeVarargs
static <E> void varargs(E... elements) { }

@SafeVarargs
static <E extends Number> void varargsWithBound(E... elements) { }
}
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
| Test.java:10:15:10:25 | annotations | annotations(byte[],byte[],java.lang.String,java.lang.Class,Test.Inner,java.lang.Class,java.lang.Class[],byte,java.lang.String[],java.lang.String[]) |
| Test.java:11:15:11:25 | annotations | annotations(byte[],byte[],java.lang.String,java.lang.Class,Test.Inner,java.lang.Class,java.lang.Class[],byte,java.lang.String[],java.lang.String[]) | annotations(byte[], byte[], String, Class<String>, Inner, Class<? extends String>, Class<String>[], byte, String[], String[]) | annotations(byte[], byte[], String, Class, Inner, Class, Class[], byte, String[], String[]) |
| Test.java:13:19:13:31 | typeVariables | typeVariables(java.util.function.Function) | typeVariables(Function<? super T,? extends R>) | typeVariables(Function) |
| Test.java:18:19:18:25 | varargs | varargs(java.lang.Object[]) | varargs(E[]) | varargs(Object[]) |
| Test.java:21:34:21:49 | varargsWithBound | varargsWithBound(java.lang.Number[]) | varargsWithBound(E[]) | varargsWithBound(Number[]) |
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import java

from Method m
where m.getFile().getBaseName() = "Test.java"
select m, m.getSignature()
select m, m.getSignature(), m.getStringSignature(), m.getErasureStringSignature()