Skip to content

Java: Add BoundedType.getTypeBound(int) #5406

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
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
7 changes: 6 additions & 1 deletion java/ql/src/semmle/code/java/Generics.qll
Original file line number Diff line number Diff line change
@@ -91,11 +91,16 @@ abstract class BoundedType extends RefType, @boundedtype {
/** Holds if this type is bounded. */
predicate hasTypeBound() { exists(TypeBound tb | tb = this.getATypeBound()) }

/** Gets the type bound for this type at the 0-based position, if any. */
TypeBound getTypeBound(int position) {
result = getATypeBound() and result.getPosition() = position
}

/** Gets a type bound for this type, if any. */
TypeBound getATypeBound() { result.getBoundedType() = this }

/** Gets the first type bound for this type, if any. */
TypeBound getFirstTypeBound() { result = getATypeBound() and result.getPosition() = 0 }
TypeBound getFirstTypeBound() { result = getTypeBound(0) }

/**
* Gets an upper type bound of this type, or `Object`
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
| generics/A.java:13:26:13:54 | new HashMap<String,Object>(...) | 0 | generics/A.java:13:38:13:43 | String |
| generics/A.java:13:26:13:54 | new HashMap<String,Object>(...) | 1 | generics/A.java:13:46:13:51 | Object |
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import default
import TestUtilities.InlineExpectationsTest

from ClassInstanceExpr cie, Expr typearg, int idx
where typearg = cie.getTypeArgument(idx)
select cie, idx, typearg
class ParameterizedClassInstanceTest extends InlineExpectationsTest {
ParameterizedClassInstanceTest() { this = "ParameterizedClassInstanceTest" }

override string getARelevantTag() { result = ["parameterizedNew"] }

override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(ClassInstanceExpr cie |
location = cie.getLocation() and
element = cie.toString() and
tag = "parameterizedNew" and
value =
concat(int idx, Expr typearg |
typearg = cie.getTypeArgument(idx)
|
idx.toString() + typearg.toString(), "," order by idx
)
)
}
}
130 changes: 92 additions & 38 deletions java/ql/test/library-tests/generics/PrintAst.expected
Original file line number Diff line number Diff line change
@@ -7,42 +7,96 @@ generics/A.java:
#-----| -2: (Generic Parameters)
# 6| 0: [TypeVariable] T
# 7| 2: [Class] B
# 10| 2: [Class] C
# 11| 3: [FieldDeclaration] A<String> f, ...;
# 11| -1: [TypeAccess] A<String>
# 11| 0: [TypeAccess] String
# 12| 4: [FieldDeclaration] A<String>.B<> b, ...;
# 12| -1: [TypeAccess] A<String>.B<>
# 12| -1: [TypeAccess] A<String>
# 12| 0: [TypeAccess] String
# 13| 5: [FieldDeclaration] Map<String,Object> m, ...;
# 13| -1: [TypeAccess] Map<String,Object>
# 13| 0: [TypeAccess] String
# 13| 1: [TypeAccess] Object
# 13| 0: [ClassInstanceExpr] new HashMap<String,Object>(...)
# 13| -3: [TypeAccess] HashMap<String,Object>
# 13| 0: [TypeAccess] String
# 13| 1: [TypeAccess] Object
# 16| 3: [Class] D
# 9| 3: [Method] noBound
#-----| 2: (Generic Parameters)
# 9| 0: [TypeVariable] R
# 9| 3: [TypeAccess] void
# 9| 5: [BlockStmt] stmt
# 10| 4: [Method] upperBound
#-----| 2: (Generic Parameters)
# 10| 0: [TypeVariable] R
# 10| 0: [TypeAccess] String
# 10| 3: [TypeAccess] void
# 10| 5: [BlockStmt] stmt
# 13| 2: [Class] C
# 14| 3: [FieldDeclaration] A<String> f, ...;
# 14| -1: [TypeAccess] A<String>
# 14| 0: [TypeAccess] String
# 15| 4: [FieldDeclaration] A<String>.B<> b, ...;
# 15| -1: [TypeAccess] A<String>.B<>
# 15| -1: [TypeAccess] A<String>
# 15| 0: [TypeAccess] String
# 16| 5: [FieldDeclaration] Map<String,Object> m, ...;
# 16| -1: [TypeAccess] Map<String,Object>
# 16| 0: [TypeAccess] String
# 16| 1: [TypeAccess] Object
# 16| 0: [ClassInstanceExpr] new HashMap<String,Object>(...)
# 16| -3: [TypeAccess] HashMap<String,Object>
# 16| 0: [TypeAccess] String
# 16| 1: [TypeAccess] Object
# 19| 3: [Class] D
#-----| -2: (Generic Parameters)
# 16| 0: [TypeVariable] V
# 16| 0: [TypeAccess] Number
# 17| 3: [FieldDeclaration] D<?> d1, ...;
# 17| -1: [TypeAccess] D<?>
# 17| 0: [WildcardTypeAccess] ? ...
# 18| 4: [FieldDeclaration] D<? extends Object> d2, ...;
# 18| -1: [TypeAccess] D<? extends Object>
# 18| 0: [WildcardTypeAccess] ? ...
# 18| 0: [TypeAccess] Object
# 19| 5: [FieldDeclaration] D<? extends Float> d3, ...;
# 19| -1: [TypeAccess] D<? extends Float>
# 19| 0: [WildcardTypeAccess] ? ...
# 19| 0: [TypeAccess] Float
# 20| 6: [FieldDeclaration] D<? super Double> d4, ...;
# 20| -1: [TypeAccess] D<? super Double>
# 20| 0: [WildcardTypeAccess] ? ...
# 20| 1: [TypeAccess] Double
# 21| 7: [BlockStmt] stmt
# 21| 0: [ExprStmt] stmt
# 21| 0: [MethodAccess] asList(...)
# 21| -1: [TypeAccess] Arrays
# 19| 0: [TypeVariable] V
# 19| 0: [TypeAccess] Number
# 21| 3: [FieldDeclaration] D<?> d1, ...;
# 21| -1: [TypeAccess] D<?>
# 21| 0: [WildcardTypeAccess] ? ...
# 22| 4: [FieldDeclaration] D<? extends Object> d2, ...;
# 22| -1: [TypeAccess] D<? extends Object>
# 22| 0: [WildcardTypeAccess] ? ...
# 22| 0: [TypeAccess] Object
# 23| 5: [FieldDeclaration] D<? extends Float> d3, ...;
# 23| -1: [TypeAccess] D<? extends Float>
# 23| 0: [WildcardTypeAccess] ? ...
# 23| 0: [TypeAccess] Float
# 24| 6: [FieldDeclaration] D<? super Double> d4, ...;
# 24| -1: [TypeAccess] D<? super Double>
# 24| 0: [WildcardTypeAccess] ? ...
# 24| 1: [TypeAccess] Double
# 25| 7: [BlockStmt] stmt
# 25| 0: [ExprStmt] stmt
# 25| 0: [MethodAccess] asList(...)
# 25| -1: [TypeAccess] Arrays
# 28| 4: [Interface] I1
# 29| 5: [Interface] I2
# 31| 6: [Class] E1
#-----| -2: (Generic Parameters)
# 31| 0: [TypeVariable] T
# 31| 0: [TypeAccess] C
# 31| 1: [TypeAccess] I1
# 31| 2: [TypeAccess] I2
# 32| 7: [Class] E2
#-----| -2: (Generic Parameters)
# 32| 0: [TypeVariable] T
# 32| 0: [TypeAccess] I2
# 32| 1: [TypeAccess] I1
# 33| 8: [Class] E3
#-----| -2: (Generic Parameters)
# 33| 0: [TypeVariable] T
# 33| 0: [TypeAccess] Object
# 33| 1: [TypeAccess] I2
# 33| 2: [TypeAccess] I1
# 35| 9: [Class] F
# 36| 2: [Method] test1
#-----| 2: (Generic Parameters)
# 36| 0: [TypeVariable] T
# 36| 0: [TypeAccess] C
# 36| 1: [TypeAccess] I1
# 36| 2: [TypeAccess] I2
# 36| 3: [TypeAccess] void
# 36| 5: [BlockStmt] stmt
# 37| 3: [Method] test2
#-----| 2: (Generic Parameters)
# 37| 0: [TypeVariable] T
# 37| 0: [TypeAccess] I2
# 37| 1: [TypeAccess] I1
# 37| 3: [TypeAccess] void
# 37| 5: [BlockStmt] stmt
# 38| 4: [Method] test3
#-----| 2: (Generic Parameters)
# 38| 0: [TypeVariable] T
# 38| 0: [TypeAccess] Object
# 38| 1: [TypeAccess] I2
# 38| 2: [TypeAccess] I1
# 38| 3: [TypeAccess] void
# 38| 5: [BlockStmt] stmt
16 changes: 10 additions & 6 deletions java/ql/test/library-tests/generics/SourceDeclaration.expected
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
| 0 | generics/A$B.class:0:0:0:0 | B<> | 7 | generics/A.java:7:8:7:8 | B |
| 0 | generics/A.class:0:0:0:0 | A<String> | 6 | generics/A.java:6:14:6:14 | A |
| 0 | generics/D.class:0:0:0:0 | D<? extends Float> | 16 | generics/A.java:16:7:16:7 | D |
| 0 | generics/D.class:0:0:0:0 | D<? extends Object> | 16 | generics/A.java:16:7:16:7 | D |
| 0 | generics/D.class:0:0:0:0 | D<? super Double> | 16 | generics/A.java:16:7:16:7 | D |
| 0 | generics/D.class:0:0:0:0 | D<?> | 16 | generics/A.java:16:7:16:7 | D |
| 0 | generics/D.class:0:0:0:0 | D<? extends Float> | 19 | generics/A.java:19:7:19:7 | D |
| 0 | generics/D.class:0:0:0:0 | D<? extends Object> | 19 | generics/A.java:19:7:19:7 | D |
| 0 | generics/D.class:0:0:0:0 | D<? super Double> | 19 | generics/A.java:19:7:19:7 | D |
| 0 | generics/D.class:0:0:0:0 | D<?> | 19 | generics/A.java:19:7:19:7 | D |
| 6 | generics/A.java:6:14:6:14 | A | 6 | generics/A.java:6:14:6:14 | A |
| 7 | generics/A.java:7:8:7:8 | B | 7 | generics/A.java:7:8:7:8 | B |
| 10 | generics/A.java:10:7:10:7 | C | 10 | generics/A.java:10:7:10:7 | C |
| 16 | generics/A.java:16:7:16:7 | D | 16 | generics/A.java:16:7:16:7 | D |
| 13 | generics/A.java:13:7:13:7 | C | 13 | generics/A.java:13:7:13:7 | C |
| 19 | generics/A.java:19:7:19:7 | D | 19 | generics/A.java:19:7:19:7 | D |
| 31 | generics/A.java:31:7:31:8 | E1 | 31 | generics/A.java:31:7:31:8 | E1 |
| 32 | generics/A.java:32:7:32:8 | E2 | 32 | generics/A.java:32:7:32:8 | E2 |
| 33 | generics/A.java:33:7:33:8 | E3 | 33 | generics/A.java:33:7:33:8 | E3 |
| 35 | generics/A.java:35:7:35:7 | F | 35 | generics/A.java:35:7:35:7 | F |
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
| file://:0:0:0:0 | ? | Object |
| file://:0:0:0:0 | ? extends Float | Float |
| file://:0:0:0:0 | ? extends Object | Object |
| file://:0:0:0:0 | ? super Double | Object |
| generics/A.java:6:16:6:16 | T | Object |
| generics/A.java:16:9:16:24 | V | Number |
36 changes: 31 additions & 5 deletions java/ql/test/library-tests/generics/TypeVarsUpperBound.ql
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
import default
import TestUtilities.InlineExpectationsTest

from BoundedType bt
where
bt.fromSource() or
exists(TypeAccess ta | ta.getType().(ParameterizedType).getATypeArgument() = bt)
select bt, bt.getUpperBoundType().toString()
private string getOrderedBounds(BoundedType bt) {
result =
concat(TypeBound bound, int position |
bound = bt.getTypeBound(position)
|
bound.getType().toString(), "," order by position
)
}

class TypeVariableBoundTest extends InlineExpectationsTest {
TypeVariableBoundTest() { this = "TypeVariableBoundTest" }

override string getARelevantTag() { result = ["bounded", "boundedAccess"] }

override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(BoundedType bt | value = getOrderedBounds(bt) |
bt.fromSource() and
location = bt.getLocation() and
element = bt.toString() and
tag = "bounded"
or
exists(TypeAccess ta |
ta.getType().(ParameterizedType).getATypeArgument() = bt and
location = ta.getLocation() and
element = ta.toString() and
tag = "boundedAccess"
)
)
}
}
33 changes: 25 additions & 8 deletions java/ql/test/library-tests/generics/generics/A.java
Original file line number Diff line number Diff line change
@@ -3,20 +3,37 @@
import java.util.HashMap;
import java.util.Map;

public class A<T> {
public class A<T> { // $bounded=
class B { }

public <R> void noBound() { } // $bounded=
public <R extends String> void upperBound() { } // $bounded=String
}

class C {
A<String> f;
A<String>.B b;
Map<String, Object> m = new HashMap<String, Object>();
Map<String, Object> m = new HashMap<String, Object>(); // $parameterizedNew=0String,1Object
}

class D<V extends Number> {
D<?> d1;
D<? extends Object> d2;
D<? extends Float> d3;
D<? super Double> d4;
class D<V extends Number> { // $bounded=Number
// Currently erroneously reports implicit Object as bound, see https://github.com/github/codeql/issues/5405
D<?> d1; // $SPURIOUS: boundedAccess=Object MISSING: boundedAccess=
D<? extends Object> d2; // $boundedAccess=Object
D<? extends Float> d3; // $boundedAccess=Float
D<? super Double> d4; // $boundedAccess=Double
{ java.util.Arrays.asList(); }
}
}

interface I1 { }
interface I2 { }

class E1<T extends C & I1 & I2> { } // $bounded=C,I1,I2
class E2<T extends I2 & I1> { } // $bounded=I2,I1
class E3<T extends Object & I2 & I1> { } // $bounded=Object,I2,I1

class F {
public <T extends C & I1 & I2> void test1() { } // $bounded=C,I1,I2
public <T extends I2 & I1> void test2() { } // $bounded=I2,I1
public <T extends Object & I2 & I1> void test3() { } // $bounded=Object,I2,I1
}