diff --git a/java/ql/src/semmle/code/java/Generics.qll b/java/ql/src/semmle/code/java/Generics.qll index 4366abbac887..07e470632281 100755 --- a/java/ql/src/semmle/code/java/Generics.qll +++ b/java/ql/src/semmle/code/java/Generics.qll @@ -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` diff --git a/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.expected b/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.expected index 3959e43df016..e69de29bb2d1 100644 --- a/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.expected +++ b/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.expected @@ -1,2 +0,0 @@ -| generics/A.java:13:26:13:54 | new HashMap(...) | 0 | generics/A.java:13:38:13:43 | String | -| generics/A.java:13:26:13:54 | new HashMap(...) | 1 | generics/A.java:13:46:13:51 | Object | diff --git a/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.ql b/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.ql index 59d8228785ff..ffee0c3003ea 100644 --- a/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.ql +++ b/java/ql/test/library-tests/generics/ParameterizedClassInstanceExpressions.ql @@ -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 + ) + ) + } +} diff --git a/java/ql/test/library-tests/generics/PrintAst.expected b/java/ql/test/library-tests/generics/PrintAst.expected index 7c4932a6f50f..6a94fc74353f 100644 --- a/java/ql/test/library-tests/generics/PrintAst.expected +++ b/java/ql/test/library-tests/generics/PrintAst.expected @@ -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 f, ...; -# 11| -1: [TypeAccess] A -# 11| 0: [TypeAccess] String -# 12| 4: [FieldDeclaration] A.B<> b, ...; -# 12| -1: [TypeAccess] A.B<> -# 12| -1: [TypeAccess] A -# 12| 0: [TypeAccess] String -# 13| 5: [FieldDeclaration] Map m, ...; -# 13| -1: [TypeAccess] Map -# 13| 0: [TypeAccess] String -# 13| 1: [TypeAccess] Object -# 13| 0: [ClassInstanceExpr] new HashMap(...) -# 13| -3: [TypeAccess] HashMap -# 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 f, ...; +# 14| -1: [TypeAccess] A +# 14| 0: [TypeAccess] String +# 15| 4: [FieldDeclaration] A.B<> b, ...; +# 15| -1: [TypeAccess] A.B<> +# 15| -1: [TypeAccess] A +# 15| 0: [TypeAccess] String +# 16| 5: [FieldDeclaration] Map m, ...; +# 16| -1: [TypeAccess] Map +# 16| 0: [TypeAccess] String +# 16| 1: [TypeAccess] Object +# 16| 0: [ClassInstanceExpr] new HashMap(...) +# 16| -3: [TypeAccess] HashMap +# 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 d2, ...; -# 18| -1: [TypeAccess] D -# 18| 0: [WildcardTypeAccess] ? ... -# 18| 0: [TypeAccess] Object -# 19| 5: [FieldDeclaration] D d3, ...; -# 19| -1: [TypeAccess] D -# 19| 0: [WildcardTypeAccess] ? ... -# 19| 0: [TypeAccess] Float -# 20| 6: [FieldDeclaration] D d4, ...; -# 20| -1: [TypeAccess] D -# 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 d2, ...; +# 22| -1: [TypeAccess] D +# 22| 0: [WildcardTypeAccess] ? ... +# 22| 0: [TypeAccess] Object +# 23| 5: [FieldDeclaration] D d3, ...; +# 23| -1: [TypeAccess] D +# 23| 0: [WildcardTypeAccess] ? ... +# 23| 0: [TypeAccess] Float +# 24| 6: [FieldDeclaration] D d4, ...; +# 24| -1: [TypeAccess] D +# 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 diff --git a/java/ql/test/library-tests/generics/SourceDeclaration.expected b/java/ql/test/library-tests/generics/SourceDeclaration.expected index fcfd4d48f656..2411f6a345a6 100644 --- a/java/ql/test/library-tests/generics/SourceDeclaration.expected +++ b/java/ql/test/library-tests/generics/SourceDeclaration.expected @@ -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 | 6 | generics/A.java:6:14:6:14 | A | -| 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 | 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 | 16 | generics/A.java:16:7:16:7 | D | +| 0 | generics/D.class:0:0:0:0 | D | 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 | +| 0 | generics/D.class:0:0:0:0 | D | 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 | diff --git a/java/ql/test/library-tests/generics/TypeVarsUpperBound.expected b/java/ql/test/library-tests/generics/TypeVarsUpperBound.expected index 68b0439b16fd..e69de29bb2d1 100644 --- a/java/ql/test/library-tests/generics/TypeVarsUpperBound.expected +++ b/java/ql/test/library-tests/generics/TypeVarsUpperBound.expected @@ -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 | diff --git a/java/ql/test/library-tests/generics/TypeVarsUpperBound.ql b/java/ql/test/library-tests/generics/TypeVarsUpperBound.ql index 83317e16ee67..7c3342ef8062 100644 --- a/java/ql/test/library-tests/generics/TypeVarsUpperBound.ql +++ b/java/ql/test/library-tests/generics/TypeVarsUpperBound.ql @@ -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" + ) + ) + } +} diff --git a/java/ql/test/library-tests/generics/generics/A.java b/java/ql/test/library-tests/generics/generics/A.java index 2cd3a4ed5c8c..94c63ec089f5 100644 --- a/java/ql/test/library-tests/generics/generics/A.java +++ b/java/ql/test/library-tests/generics/generics/A.java @@ -3,20 +3,37 @@ import java.util.HashMap; import java.util.Map; -public class A { +public class A { // $bounded= class B { } + + public void noBound() { } // $bounded= + public void upperBound() { } // $bounded=String } class C { A f; A.B b; - Map m = new HashMap(); + Map m = new HashMap(); // $parameterizedNew=0String,1Object } -class D { - D d1; - D d2; - D d3; - D d4; +class D { // $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 d2; // $boundedAccess=Object + D d3; // $boundedAccess=Float + D d4; // $boundedAccess=Double { java.util.Arrays.asList(); } -} \ No newline at end of file +} + +interface I1 { } +interface I2 { } + +class E1 { } // $bounded=C,I1,I2 +class E2 { } // $bounded=I2,I1 +class E3 { } // $bounded=Object,I2,I1 + +class F { + public void test1() { } // $bounded=C,I1,I2 + public void test2() { } // $bounded=I2,I1 + public void test3() { } // $bounded=Object,I2,I1 +}