Skip to content

Commit 3ae3692

Browse files
committed
test: fail the build when TablesNamesFinder misses a visitor dispatch method
TablesNamesFinder dispatches through 8 visitor interfaces. Abstract dispatch methods are compiler-enforced, but the interfaces progressively gain default ones (e.g. SelectVisitor#visit(PivotQuery, context)), and for a default declared by a single interface a missing implementation compiles silently and loses the tables of that node type without any test failing. Pin the complete post-#2479/#2517 state (214 methods). Signed-off-by: 付典 <fudianchn@gmail.com>
1 parent 8397d3a commit 3ae3692

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package net.sf.jsqlparser.util;
2+
3+
import static org.junit.jupiter.api.Assertions.assertTrue;
4+
5+
import java.lang.reflect.Method;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Set;
9+
10+
import org.junit.jupiter.api.Test;
11+
12+
/**
13+
* TablesNamesFinder collects tables by dispatching through the visitor interfaces it declares. Most
14+
* dispatch methods are abstract, so a missing implementation fails the build already. But the
15+
* interfaces progressively gain default methods (e.g. SelectVisitor#visit(PivotQuery, context)
16+
* returns null), and for those a missing TablesNamesFinder implementation compiles silently and
17+
* loses the tables of that node type without any test failing.
18+
*
19+
* This test fails the build when a declared visitor interface has a dispatch method visit(Node,
20+
* context) for an AST node that TablesNamesFinder does not implement itself. It does not catch
21+
* empty-shell implementations (an override that visits nothing) nor visitor families the finder
22+
* does not declare at all: both remain manual review duties.
23+
*/
24+
public class TablesNamesFinderCompletenessTest {
25+
26+
// GroupByElement: ExpressionVisitor's default forwards the group-by expressions and the
27+
// grouping sets to the visitor, TablesNamesFinder relies on it from visit(PlainSelect).
28+
private static final Set<String> INHERITED_DISPATCH_NODES =
29+
Set.of("net.sf.jsqlparser.statement.select.GroupByElement");
30+
31+
@Test
32+
void testImplementsEveryDispatchMethodOfEveryDeclaredVisitorInterface()
33+
throws NoSuchMethodException {
34+
List<String> gaps = new ArrayList<>();
35+
for (Class<?> visitorInterface : TablesNamesFinder.class.getInterfaces()) {
36+
for (Method dispatch : visitorInterface.getMethods()) {
37+
if (!dispatch.getName().equals("visit") || dispatch.getParameterCount() != 2) {
38+
continue;
39+
}
40+
Class<?> nodeType = dispatch.getParameterTypes()[0];
41+
// skips bulk dispatch helpers carrying a java.util collection
42+
if (!nodeType.getName().startsWith("net.sf.jsqlparser.")) {
43+
continue;
44+
}
45+
if (INHERITED_DISPATCH_NODES.contains(nodeType.getName())) {
46+
continue;
47+
}
48+
Method implementation = TablesNamesFinder.class.getMethod(dispatch.getName(),
49+
dispatch.getParameterTypes());
50+
if (implementation.getDeclaringClass() != TablesNamesFinder.class) {
51+
gaps.add(visitorInterface.getSimpleName() + "#" + dispatch.getName() + "("
52+
+ nodeType.getSimpleName() + ", context)");
53+
}
54+
}
55+
}
56+
57+
assertTrue(gaps.isEmpty(), () -> "TablesNamesFinder must implement every dispatch"
58+
+ " method of the visitor interfaces it declares, otherwise the tables of that"
59+
+ " node type are silently not collected. Missing:\n" + String.join("\n", gaps)
60+
+ "\nImplement visit(Node, context) in TablesNamesFinder, or - when the"
61+
+ " inherited default already forwards every child - document the node in"
62+
+ " INHERITED_DISPATCH_NODES.");
63+
}
64+
}

0 commit comments

Comments
 (0)