Sonar的TryStatementTree#resourceList何时返回非VariableTree类型的Tree?
Great question! Let's unpack this—you're right to dig into the edge cases here, and there's a key Java language feature you might have overlooked that explains this API design.
The Java 9+ Try-With-Resources Enhancement
First, let's recall the Java Language Specification: prior to Java 9, try-with-resources blocks only allowed variable declarations (e.g., try (AutoCloseable res = new MyCloseable()) { ... }). But Java 9 expanded this to allow any expression that evaluates to an AutoCloseable instance, including:
- Pre-declared variables:
AutoCloseable myRes = new MyCloseable(); try (myRes) { // Valid Java 9+ syntax // ... } - Method calls or other expressions:
try (MyCloseable.create()) { // Direct method invocation as resource // ... }
In the SonarQube AST, these non-declaration resources are not represented as VariableTree instances. Instead:
- A pre-declared variable like
myResbecomes anIdentifierTree - A method call like
MyCloseable.create()becomes aMethodInvocationTree
This means your current filter (resource instanceof VariableTree) will return false for these valid Java 9+ resources, which is exactly the scenario you were wondering about.
Why SonarQube's API Returns ListTree<Tree>
The TryStatementTree.resourceList() returns a generic ListTree<Tree> precisely because the Java language allows more than just variable declarations in try-with-resources blocks post-Java 9. The API is designed to be forward-compatible and cover all valid syntax permitted by the JLS, rather than restricting itself to the older Java 7-style variable declarations.
Your observation that regular try blocks (without resources) don't trigger the stream logic is correct: when there are no resources, resourceList() returns an empty list, so the stream operations do nothing. The visitTryStatement method is still called, but there's no data to process.
Fixing Your Rule to Cover All Cases
If your goal is to inspect all resources in try-with-resources blocks (regardless of Java version), you'll need to handle multiple Tree types. For example:
@Override public void visitTryStatement(TryStatementTree tree) { tree.resourceList().stream() .forEach(resource -> { if (resource instanceof VariableTree varTree) { // Handle Java 7+ variable declaration resources String varName = varTree.simpleName().name(); SomeClass.handleVariableDeclarationCase(varName); } else if (resource instanceof IdentifierTree idTree) { // Handle Java 9+ pre-declared variable resources String varName = idTree.name(); SomeClass.handleExistingVariableCase(varName); } else if (resource instanceof MethodInvocationTree methodTree) { // Handle method call resources (e.g., try (createRes())) String methodName = methodTree.methodSelect().toString(); SomeClass.handleMethodInvocationCase(methodName); } // Add other expression types as needed }); // Don't forget to call super to visit child nodes super.visitTryStatement(tree); }
Wrap-Up
You weren't missing a "tricky" syntax edge case from older Java versions—instead, the API design accounts for the expanded try-with-resources syntax introduced in Java 9. That generic Tree return type ensures the API can support all valid resource expressions allowed by the language.
内容的提问来源于stack exchange,提问作者toniedzwiedz

