diff --git a/java/ql/lib/change-notes/2024-01-24-anonymous-diamond.md b/java/ql/lib/change-notes/2024-01-24-anonymous-diamond.md new file mode 100644 index 000000000000..0933d40a8c7a --- /dev/null +++ b/java/ql/lib/change-notes/2024-01-24-anonymous-diamond.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* Fixed `ClassInstanceExpr::isDiamond` not working for anonymous classes. diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 74f37a4a4514..9e4ff48ea0de 100644 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -1243,7 +1243,14 @@ class ClassInstanceExpr extends Expr, ConstructorCall, @classinstancexpr { * empty type argument list of the form `<>`. */ predicate isDiamond() { - this.getType() instanceof ParameterizedClass and + ( + this.getType() instanceof ParameterizedClass + or + this.getAnonymousClass().getASupertype() instanceof ParameterizedType and + // Ignore Kotlin code; otherwise seems to erroneously match `object` expression with explicit + // type arguments, possibly because extractor is not properly extracting the type args? + not this.getCompilationUnit().isKotlinSourceFile() + ) and not exists(this.getATypeArgument()) } diff --git a/java/ql/test/library-tests/java7/Diamond/Diamond.java b/java/ql/test/library-tests/java7/Diamond/Diamond.java index e24803ba618e..5ad33883b8c4 100644 --- a/java/ql/test/library-tests/java7/Diamond/Diamond.java +++ b/java/ql/test/library-tests/java7/Diamond/Diamond.java @@ -8,13 +8,32 @@ public class Diamond { // normal parameterized class instance expressions List list = new ArrayList(); - Map map = new HashMap(); - + Map map = new HashMap(); + // same, but with diamond List diamond_list = new ArrayList<>(); Map diamond_map = new HashMap<>(); - + + // anonymous without diamond + Object obj = new Object() {}; + Runnable runnable = new Runnable() { + @Override + public void run() { + } + }; + List list2 = new ArrayList() {}; + + // anonymous with diamond (supported since Java 9) + List list3 = new ArrayList<>() {}; + Map map2 = new HashMap<>() { + @Override + public String toString() { + return "custom map"; + } + }; + // other class instance expressions List l = new ArrayList(); + List l2 = new ArrayList() {}; Error e = new Error(); } diff --git a/java/ql/test/library-tests/java7/Diamond/Diamonds.expected b/java/ql/test/library-tests/java7/Diamond/Diamonds.expected index abc2e9176142..0be076e1a2b6 100644 --- a/java/ql/test/library-tests/java7/Diamond/Diamonds.expected +++ b/java/ql/test/library-tests/java7/Diamond/Diamonds.expected @@ -1,2 +1,4 @@ | Diamond.java:14:31:14:47 | new ArrayList(...) | | Diamond.java:15:36:15:50 | new HashMap(...) | +| Diamond.java:27:24:27:43 | new (...) | +| Diamond.java:28:29:33:2 | new (...) | diff --git a/java/ql/test/library-tests/java7/Diamond/PrintAst.expected b/java/ql/test/library-tests/java7/Diamond/PrintAst.expected index 5e1ceb8f846a..9c9954cfe77f 100644 --- a/java/ql/test/library-tests/java7/Diamond/PrintAst.expected +++ b/java/ql/test/library-tests/java7/Diamond/PrintAst.expected @@ -31,11 +31,58 @@ Diamond.java: # 15| 1: [TypeAccess] Object # 15| 0: [ClassInstanceExpr] new HashMap(...) # 15| -3: [TypeAccess] HashMap -# 18| 7: [FieldDeclaration] List<> l; -# 18| -1: [TypeAccess] List<> -# 18| 0: [ClassInstanceExpr] new ArrayList<>(...) -# 18| -3: [TypeAccess] ArrayList<> -# 19| 8: [FieldDeclaration] Error e; -# 19| -1: [TypeAccess] Error -# 19| 0: [ClassInstanceExpr] new Error(...) -# 19| -3: [TypeAccess] Error +# 18| 7: [FieldDeclaration] Object obj; +# 18| -1: [TypeAccess] Object +# 18| 0: [ClassInstanceExpr] new (...) +# 18| -4: [AnonymousClass] new Object(...) { ... } +# 18| -3: [TypeAccess] Object +# 19| 8: [FieldDeclaration] Runnable runnable; +# 19| -1: [TypeAccess] Runnable +# 19| 0: [ClassInstanceExpr] new (...) +# 19| -4: [AnonymousClass] new Runnable(...) { ... } +# 21| 2: [Method] run +#-----| 1: (Annotations) +# 20| 1: [Annotation] Override +# 21| 3: [TypeAccess] void +# 21| 5: [BlockStmt] { ... } +# 19| -3: [TypeAccess] Runnable +# 24| 9: [FieldDeclaration] List list2; +# 24| -1: [TypeAccess] List +# 24| 0: [TypeAccess] Integer +# 24| 0: [ClassInstanceExpr] new (...) +# 24| -4: [AnonymousClass] new ArrayList(...) { ... } +# 24| -3: [TypeAccess] ArrayList +# 24| 0: [TypeAccess] Integer +# 27| 10: [FieldDeclaration] List list3; +# 27| -1: [TypeAccess] List +# 27| 0: [TypeAccess] Integer +# 27| 0: [ClassInstanceExpr] new (...) +# 27| -4: [AnonymousClass] new ArrayList(...) { ... } +# 27| -3: [TypeAccess] ArrayList +# 28| 11: [FieldDeclaration] Map map2; +# 28| -1: [TypeAccess] Map +# 28| 0: [TypeAccess] String +# 28| 1: [TypeAccess] Object +# 28| 0: [ClassInstanceExpr] new (...) +# 28| -4: [AnonymousClass] new HashMap(...) { ... } +# 30| 2: [Method] toString +#-----| 1: (Annotations) +# 29| 1: [Annotation] Override +# 30| 3: [TypeAccess] String +# 30| 5: [BlockStmt] { ... } +# 31| 0: [ReturnStmt] return ... +# 31| 0: [StringLiteral] "custom map" +# 28| -3: [TypeAccess] HashMap +# 36| 12: [FieldDeclaration] List<> l; +# 36| -1: [TypeAccess] List<> +# 36| 0: [ClassInstanceExpr] new ArrayList<>(...) +# 36| -3: [TypeAccess] ArrayList<> +# 37| 13: [FieldDeclaration] List<> l2; +# 37| -1: [TypeAccess] List<> +# 37| 0: [ClassInstanceExpr] new (...) +# 37| -4: [AnonymousClass] new ArrayList<>(...) { ... } +# 37| -3: [TypeAccess] ArrayList<> +# 38| 14: [FieldDeclaration] Error e; +# 38| -1: [TypeAccess] Error +# 38| 0: [ClassInstanceExpr] new Error(...) +# 38| -3: [TypeAccess] Error diff --git a/java/ql/test/library-tests/java7/Diamond/options b/java/ql/test/library-tests/java7/Diamond/options new file mode 100644 index 000000000000..ffb580faf535 --- /dev/null +++ b/java/ql/test/library-tests/java7/Diamond/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -source 9 -target 9