From 916b1e959e15cee53d99333c8a5d137a68fb1f54 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 18 Mar 2024 15:56:10 +0000 Subject: [PATCH 1/5] Java: Add a test for MissingEnumInSwitch --- .../MissingEnumInSwitch.expected | 10 + .../MissingEnumInSwitch.qlref | 1 + .../Statements/MissingEnumInSwitch/Test.java | 187 ++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected create mode 100644 java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.qlref create mode 100644 java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/Test.java diff --git a/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected new file mode 100644 index 000000000000..5daf2fc5b8a0 --- /dev/null +++ b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected @@ -0,0 +1,10 @@ +| Test.java:8:5:8:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 22 more. | Test.java:4:27:4:27 | B | B | Test.java:4:25:4:25 | C | C | Test.java:4:45:4:45 | D | D | +| Test.java:11:5:11:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 21 more. | Test.java:4:25:4:25 | C | C | Test.java:4:45:4:45 | D | D | Test.java:4:15:4:15 | E | E | +| Test.java:15:5:15:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 20 more. | Test.java:4:45:4:45 | D | D | Test.java:4:15:4:15 | E | E | Test.java:4:43:4:43 | F | F | +| Test.java:20:5:20:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 19 more. | Test.java:4:15:4:15 | E | E | Test.java:4:43:4:43 | F | F | Test.java:4:49:4:49 | G | G | +| Test.java:26:5:26:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 18 more. | Test.java:4:43:4:43 | F | F | Test.java:4:49:4:49 | G | G | Test.java:4:35:4:35 | H | H | +| Test.java:33:5:33:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 2 more. | Test.java:4:21:4:21 | V | V | Test.java:4:13:4:13 | W | W | Test.java:4:23:4:23 | X | X | +| Test.java:56:5:56:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 1 more. | Test.java:4:13:4:13 | W | W | Test.java:4:23:4:23 | X | X | Test.java:4:11:4:11 | Y | Y | +| Test.java:80:5:80:13 | switch (...) | Switch statement does not have a case for $@, $@ or $@. | Test.java:4:23:4:23 | X | X | Test.java:4:11:4:11 | Y | Y | Test.java:4:33:4:33 | Z | Z | +| Test.java:105:5:105:13 | switch (...) | Switch statement does not have a case for $@ or $@. | Test.java:4:11:4:11 | Y | Y | Test.java:4:33:4:33 | Z | Z | Test.java:4:11:4:11 | Y | Y | +| Test.java:131:5:131:13 | switch (...) | Switch statement does not have a case for $@. | Test.java:4:33:4:33 | Z | Z | Test.java:4:33:4:33 | Z | Z | Test.java:4:33:4:33 | Z | Z | diff --git a/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.qlref b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.qlref new file mode 100644 index 000000000000..10f1b3e8be23 --- /dev/null +++ b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.qlref @@ -0,0 +1 @@ +Likely Bugs/Statements/MissingEnumInSwitch.ql diff --git a/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/Test.java b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/Test.java new file mode 100644 index 000000000000..2f39918ead4c --- /dev/null +++ b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/Test.java @@ -0,0 +1,187 @@ +public class Test { + private enum MyEnum { + // A..Z in random order + N,R,S,Y,W,E,K,I,V,X,C,B,O,J,Z,H,T,P,A,F,D,M,G,U,L,Q + } + + public void use(MyEnum e) { + switch(e) { + case A: break; + } + switch(e) { + case A: break; + case B: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + case E: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + case E: break; + case F: break; + case G: break; + case H: break; + case I: break; + case J: break; + case K: break; + case L: break; + case M: break; + case N: break; + case O: break; + case P: break; + case Q: break; + case R: break; + case S: break; + case T: break; + case U: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + case E: break; + case F: break; + case G: break; + case H: break; + case I: break; + case J: break; + case K: break; + case L: break; + case M: break; + case N: break; + case O: break; + case P: break; + case Q: break; + case R: break; + case S: break; + case T: break; + case U: break; + case V: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + case E: break; + case F: break; + case G: break; + case H: break; + case I: break; + case J: break; + case K: break; + case L: break; + case M: break; + case N: break; + case O: break; + case P: break; + case Q: break; + case R: break; + case S: break; + case T: break; + case U: break; + case V: break; + case W: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + case E: break; + case F: break; + case G: break; + case H: break; + case I: break; + case J: break; + case K: break; + case L: break; + case M: break; + case N: break; + case O: break; + case P: break; + case Q: break; + case R: break; + case S: break; + case T: break; + case U: break; + case V: break; + case W: break; + case X: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + case E: break; + case F: break; + case G: break; + case H: break; + case I: break; + case J: break; + case K: break; + case L: break; + case M: break; + case N: break; + case O: break; + case P: break; + case Q: break; + case R: break; + case S: break; + case T: break; + case U: break; + case V: break; + case W: break; + case X: break; + case Y: break; + } + switch(e) { + case A: break; + case B: break; + case C: break; + case D: break; + case E: break; + case F: break; + case G: break; + case H: break; + case I: break; + case J: break; + case K: break; + case L: break; + case M: break; + case N: break; + case O: break; + case P: break; + case Q: break; + case R: break; + case S: break; + case T: break; + case U: break; + case V: break; + case W: break; + case X: break; + case Y: break; + case Z: break; + } + } +} From 60b5e499050d75587bf65f5e0e3877caccd326a3 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 18 Mar 2024 15:56:21 +0000 Subject: [PATCH 2/5] Java: Limit the amount of results that MissingEnumInSwitch produces per switch The tool status page warns: An analysis file contained multiple alerts that included more related locations than our allowed limit of 100. These alerts correspond to the rule java/missing-case-in-switch. Only 100 locations were stored for these alerts. --- .../Statements/MissingEnumInSwitch.ql | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql b/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql index dfea3ad72d95..0ebe0ebb0251 100644 --- a/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql +++ b/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql @@ -13,10 +13,51 @@ import java -from SwitchStmt switch, EnumType enum, EnumConstant missing -where - switch.getExpr().getType() = enum and - missing.getDeclaringType() = enum and +EnumConstant nthMissing(SwitchStmt switch, int index) { not exists(switch.getDefaultCase()) and - not switch.getAConstCase().getValue() = missing.getAnAccess() -select switch, "Switch statement does not have a case for $@.", missing, missing.getName() + exists(EnumType enum | + switch.getExpr().getType() = enum and + result = + rank[index](EnumConstant ec | + ec.getDeclaringType() = enum and + not switch.getAConstCase().getValue() = ec.getAnAccess() + | + ec order by ec.getName() + ) + ) +} + +predicate first3(string msg, SwitchStmt switch, EnumConstant e1, EnumConstant e2, EnumConstant e3) { + exists(int n | n = strictcount(nthMissing(switch, _)) | + if n > 3 + then msg = "Switch statement does not have a case for $@, $@, $@ or " + (n - 3) + " more." + else msg = "Switch statement does not have a case for $@, $@ or $@." + ) and + e1 = nthMissing(switch, 1) and + e2 = nthMissing(switch, 2) and + e3 = nthMissing(switch, 3) +} + +predicate only2(string msg, SwitchStmt switch, EnumConstant e1, EnumConstant e2) { + msg = "Switch statement does not have a case for $@ or $@." and + e1 = nthMissing(switch, 1) and + e2 = nthMissing(switch, 2) +} + +predicate only1(string msg, SwitchStmt switch, EnumConstant e) { + msg = "Switch statement does not have a case for $@." and + e = nthMissing(switch, 1) +} + +from string msg, SwitchStmt switch, EnumConstant e1, EnumConstant e2, EnumConstant e3 +where + if first3(_, switch, _, _, _) + then first3(msg, switch, e1, e2, e3) + else + if only2(_, switch, _, _) + then ( + only2(msg, switch, e1, e2) and e1 = e3 + ) else ( + only1(msg, switch, e1) and e1 = e2 and e1 = e3 + ) +select switch, msg, e1, e1.getName(), e2, e2.getName(), e3, e3.getName() From 59ae6dd5f576456c77c94307f655de82bc3be32e Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Wed, 27 Mar 2024 15:07:58 +0000 Subject: [PATCH 3/5] Java: Add a couple of Oxford commas --- java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql b/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql index 0ebe0ebb0251..d1cdb8bdfbbb 100644 --- a/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql +++ b/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql @@ -30,8 +30,8 @@ EnumConstant nthMissing(SwitchStmt switch, int index) { predicate first3(string msg, SwitchStmt switch, EnumConstant e1, EnumConstant e2, EnumConstant e3) { exists(int n | n = strictcount(nthMissing(switch, _)) | if n > 3 - then msg = "Switch statement does not have a case for $@, $@, $@ or " + (n - 3) + " more." - else msg = "Switch statement does not have a case for $@, $@ or $@." + then msg = "Switch statement does not have a case for $@, $@, $@, or " + (n - 3) + " more." + else msg = "Switch statement does not have a case for $@, $@, or $@." ) and e1 = nthMissing(switch, 1) and e2 = nthMissing(switch, 2) and From fda3c9261252245ef649412d4b4d1eed7fdc41b2 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Wed, 27 Mar 2024 15:12:55 +0000 Subject: [PATCH 4/5] Java: Add a changenote for the MissingEnumInSwitch change --- java/ql/src/change-notes/2024-03-27-MissingEnumInSwitch.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/src/change-notes/2024-03-27-MissingEnumInSwitch.md diff --git a/java/ql/src/change-notes/2024-03-27-MissingEnumInSwitch.md b/java/ql/src/change-notes/2024-03-27-MissingEnumInSwitch.md new file mode 100644 index 000000000000..b1531dab6558 --- /dev/null +++ b/java/ql/src/change-notes/2024-03-27-MissingEnumInSwitch.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* The `java/missing-case-in-switch` query now gives only a single alert for each switch statement, giving some examples of the missing cases as well as a count of how many are missing. From b6a1266ade2eb38572726a053b28b10c76894334 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Wed, 27 Mar 2024 15:35:51 +0000 Subject: [PATCH 5/5] Java: Accept test changes for MissingEnumInSwitch Oxford commas --- .../MissingEnumInSwitch.expected | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected index 5daf2fc5b8a0..deea6bde7308 100644 --- a/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected +++ b/java/ql/test/query-tests/Likely Bugs/Statements/MissingEnumInSwitch/MissingEnumInSwitch.expected @@ -1,10 +1,10 @@ -| Test.java:8:5:8:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 22 more. | Test.java:4:27:4:27 | B | B | Test.java:4:25:4:25 | C | C | Test.java:4:45:4:45 | D | D | -| Test.java:11:5:11:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 21 more. | Test.java:4:25:4:25 | C | C | Test.java:4:45:4:45 | D | D | Test.java:4:15:4:15 | E | E | -| Test.java:15:5:15:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 20 more. | Test.java:4:45:4:45 | D | D | Test.java:4:15:4:15 | E | E | Test.java:4:43:4:43 | F | F | -| Test.java:20:5:20:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 19 more. | Test.java:4:15:4:15 | E | E | Test.java:4:43:4:43 | F | F | Test.java:4:49:4:49 | G | G | -| Test.java:26:5:26:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 18 more. | Test.java:4:43:4:43 | F | F | Test.java:4:49:4:49 | G | G | Test.java:4:35:4:35 | H | H | -| Test.java:33:5:33:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 2 more. | Test.java:4:21:4:21 | V | V | Test.java:4:13:4:13 | W | W | Test.java:4:23:4:23 | X | X | -| Test.java:56:5:56:13 | switch (...) | Switch statement does not have a case for $@, $@, $@ or 1 more. | Test.java:4:13:4:13 | W | W | Test.java:4:23:4:23 | X | X | Test.java:4:11:4:11 | Y | Y | -| Test.java:80:5:80:13 | switch (...) | Switch statement does not have a case for $@, $@ or $@. | Test.java:4:23:4:23 | X | X | Test.java:4:11:4:11 | Y | Y | Test.java:4:33:4:33 | Z | Z | +| Test.java:8:5:8:13 | switch (...) | Switch statement does not have a case for $@, $@, $@, or 22 more. | Test.java:4:27:4:27 | B | B | Test.java:4:25:4:25 | C | C | Test.java:4:45:4:45 | D | D | +| Test.java:11:5:11:13 | switch (...) | Switch statement does not have a case for $@, $@, $@, or 21 more. | Test.java:4:25:4:25 | C | C | Test.java:4:45:4:45 | D | D | Test.java:4:15:4:15 | E | E | +| Test.java:15:5:15:13 | switch (...) | Switch statement does not have a case for $@, $@, $@, or 20 more. | Test.java:4:45:4:45 | D | D | Test.java:4:15:4:15 | E | E | Test.java:4:43:4:43 | F | F | +| Test.java:20:5:20:13 | switch (...) | Switch statement does not have a case for $@, $@, $@, or 19 more. | Test.java:4:15:4:15 | E | E | Test.java:4:43:4:43 | F | F | Test.java:4:49:4:49 | G | G | +| Test.java:26:5:26:13 | switch (...) | Switch statement does not have a case for $@, $@, $@, or 18 more. | Test.java:4:43:4:43 | F | F | Test.java:4:49:4:49 | G | G | Test.java:4:35:4:35 | H | H | +| Test.java:33:5:33:13 | switch (...) | Switch statement does not have a case for $@, $@, $@, or 2 more. | Test.java:4:21:4:21 | V | V | Test.java:4:13:4:13 | W | W | Test.java:4:23:4:23 | X | X | +| Test.java:56:5:56:13 | switch (...) | Switch statement does not have a case for $@, $@, $@, or 1 more. | Test.java:4:13:4:13 | W | W | Test.java:4:23:4:23 | X | X | Test.java:4:11:4:11 | Y | Y | +| Test.java:80:5:80:13 | switch (...) | Switch statement does not have a case for $@, $@, or $@. | Test.java:4:23:4:23 | X | X | Test.java:4:11:4:11 | Y | Y | Test.java:4:33:4:33 | Z | Z | | Test.java:105:5:105:13 | switch (...) | Switch statement does not have a case for $@ or $@. | Test.java:4:11:4:11 | Y | Y | Test.java:4:33:4:33 | Z | Z | Test.java:4:11:4:11 | Y | Y | | Test.java:131:5:131:13 | switch (...) | Switch statement does not have a case for $@. | Test.java:4:33:4:33 | Z | Z | Test.java:4:33:4:33 | Z | Z | Test.java:4:33:4:33 | Z | Z |