When does Sonar's TryStatementTree#resourceList return a Tree that isn't a VariableTree?

huangapple go评论63阅读模式
英文:

When does Sonar's TryStatementTree#resourceList return a Tree that isn't a VariableTree?

问题

我在查看一些 SonarQube 插件的代码时,遇到了 org.sonar.plugins.java.api.tree.TryStatementTree 接口。其中实现的一条规则涉及检查在 try-with-resources 块中定义的变量。

观察 TryStatementTree#resourceList,我可以看到它返回一个 ListTree<Tree>。可以通过多种方式迭代它。在某种情况下,插件会检查声明的变量的名称。这时,Tree 被强制转换为 VariableTree,因为 Tree 是一个更通用的接口,不能通过 IdentifierTree 访问变量名。

这是我正在查看的代码中的转换部分,这个类继承自 BaseTreeVisitor 并实现了 JavaFileScanner

@Override
public void visitTryStatement(TryStatementTree tree) {
  // ...
  tree.resourceList().stream()
     .filter(resource -> resource instanceof VariableTree) // 这个条件有可能为false吗?
     .map(VariableTree.class::cast)
     .map(resource -> resource.simpleName().name())
     .forEach(SomeClass::handleThisCase);
  // ...
}

查阅Java语言规范,我想不出在 try 语句中会有什么东西代替资源声明,而不是标识符列表。

我认为这可能与需要表示不存在的资源定义或类似的情况有关,所以我对它进行了一些单元测试。

try { // 这里没有资源声明
  // ...
} catch (SomeException ex) {
  // ...
}

但在这种情况下,方法根本不会被调用,我猜这是由 BaseTreeVisitor 处理的。

我一直在尝试想出使转换不可能的示例,但我想到的一切要么无法编译,要么从不遵循这个执行路径。

我是否忽略了一种使用更通用的 Tree 更好的情况?还是这个选择源于库中接口的结构方式?这似乎并不受任何超级接口(TryStatementTree -> StatementTree -> Tree)的强制。resourceList 是由 TryStatementTree 本身定义的。

英文:

I came across the org.sonar.plugins.java.api.tree.TryStatementTree interface while going over some code for a SonarQube plugin. One of the rules implemented goes over the variables defined in a try-with-resources block.

Looking at the TryStatementTree#resourceList, I can see that it returns a ListTree<Tree>. This can be iterated over in a number of ways. In one case, the plugin inspects the name of the variable declared. When this happens, the Tree is cast to VariableTree because Tree is a more generic interface that doesn't provide access to variable names via IdentifierTree.

This is where the cast is in the code I'm looking at, a class that extends BaseTreeVisitor and implements JavaFileScanner.

@Override
public void visitTryStatement(TryStatementTree tree) {
  // ...
  tree.resourceList().stream()
     .filter(resource -> resource instanceof VariableTree) // Is it possible for this condition to evaluate to false? 
     .map(VariableTree.class::cast)
     .map(resource -> resource.simpleName().name())
     .forEach(SomeClass::handleThisCase);
  // ...
}

Looking at the Java Language Standard, I can't think of a situation when a try statement would have something in place of the resource declaration that isn't a list of identifiers.

I thought this had to do with the need to represent a non-present resource definitions or something similar so I threw a few unit test cases at it.

try { // No resource declaration here
  // ...
} catch (SomeException ex) {
  // ...
}

but in this case the method isn't called at all, which I guess is handled by BaseTreeVisitor.

I've been trying to come up with examples that would make the cast impossible but everything I've come up with either doesn't compile or never follows this execution path.

Am I missing a way to write a try statement that makes the more generic Tree the better choice? Or does this choice stem from the way the interfaces in the library have been structured? It doesn't seem to be enforced by any of the super-interfaces (TryStatementTree -> StatementTree -> Tree). resourceList is defined by TryStatementTree itself.

答案1

得分: 0

Ironically, I stumbled upon the answer in the SonarQube documentation minutes after posting the question.

It turns out there's a method that works exactly as I expected it to, TryStatementTree#resource, which was deprecated and then removed in favour of TryStatementTree#resourceList, which is exactly what my code uses.

Deprecated method org.sonar.plugins.java.api.tree.TryStatementTree.resources() has been removed, in favor of org.sonar.plugins.java.api.tree.TryStatementTree.resourceList(), as Java 9 allows other trees than VariableTree to be placed as resources in try-with-resources statements.

Here's an example:

SomeAutoCloseableClass myResource = obtainOneSomehow(); try (myResurce) { // No resource declaration here, just an identifier // ... } catch (SomeException ex) { // ... }

My project is compiled with the source level set to Java 8 compatibility, which should be amended and a new unit test case should be added to make sure the newly possible way to write a try-with-resources block is handled.

英文:

Ironically, I stumbled upon the answer in the SonarQube documentation minutes after posting the question.

It turns out there's a method that works exactly as I expected it to, TryStatementTree#resource, which was deprecated and then removed in favour of TryStatementTree#resourceList, which is exactly what my code uses.

> Deprecated method org.sonar.plugins.java.api.tree.TryStatementTree.resources() has been removed, in favor of org.sonar.plugins.java.api.tree.TryStatementTree.resourceList(), as Java 9 allows other trees than VariableTree to be placed as resources in try-with-resources statements.

Here's an example:

SomeAutoCloseableClass myResource = obtainOneSomehow();
try (myResurce) { // No resource declaration here, just an identifier
  // ...
} catch (SomeException ex) {
  // ...
}

My project is compiled with the source level set to Java 8 compatibility, which should be amended and a new unit test case should be added to make sure the newly possible way to write a try-with-resources block is handled.

huangapple
  • 本文由 发表于 2020年4月9日 23:10:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/61124346.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定