|
5 | 5 | import com.sun.source.tree.ArrayAccessTree; |
6 | 6 | import com.sun.source.tree.ArrayTypeTree; |
7 | 7 | import com.sun.source.tree.AssertTree; |
| 8 | +import com.sun.source.tree.BlockTree; |
8 | 9 | import com.sun.source.tree.BinaryTree; |
9 | 10 | import com.sun.source.tree.CaseTree; |
10 | 11 | import com.sun.source.tree.CatchTree; |
|
35 | 36 | import com.sun.source.tree.VariableTree; |
36 | 37 | import com.sun.source.tree.WhileLoopTree; |
37 | 38 | import com.sun.source.util.TreePath; |
| 39 | +import com.sun.source.util.TreeScanner; |
38 | 40 |
|
39 | 41 | import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey; |
40 | 42 | import org.checkerframework.checker.formatter.qual.FormatMethod; |
| 43 | +import org.checkerframework.checker.nullness.qual.IfNullThrows; |
41 | 44 | import org.checkerframework.checker.nullness.qual.NonNull; |
42 | 45 | import org.checkerframework.checker.nullness.qual.Nullable; |
43 | 46 | import org.checkerframework.common.basetype.BaseTypeChecker; |
@@ -600,9 +603,49 @@ public void processMethodTree(String className, MethodTree tree) { |
600 | 603 | } |
601 | 604 | } |
602 | 605 |
|
| 606 | + // @IfNullThrows well-formedness: method must throw (e.g. on null param) |
| 607 | + checkIfNullThrowsEnforced(tree); |
| 608 | + |
603 | 609 | super.processMethodTree(className, tree); |
604 | 610 | } |
605 | 611 |
|
| 612 | + /** |
| 613 | + * Reports an error if the method has {@link IfNullThrows} on any parameter but its body does |
| 614 | + * not contain a throw statement (so the contract cannot be satisfied). |
| 615 | + */ |
| 616 | + private void checkIfNullThrowsEnforced(MethodTree tree) { |
| 617 | + ExecutableElement methodElement = TreeUtils.elementFromDeclaration(tree); |
| 618 | + if (methodElement.getModifiers().contains(javax.lang.model.element.Modifier.ABSTRACT) |
| 619 | + || methodElement.getModifiers().contains(javax.lang.model.element.Modifier.NATIVE)) { |
| 620 | + return; |
| 621 | + } |
| 622 | + boolean hasIfNullThrows = false; |
| 623 | + for (Element param : methodElement.getParameters()) { |
| 624 | + if (atypeFactory.getDeclAnnotation(param, IfNullThrows.class) != null) { |
| 625 | + hasIfNullThrows = true; |
| 626 | + break; |
| 627 | + } |
| 628 | + } |
| 629 | + if (!hasIfNullThrows) { |
| 630 | + return; |
| 631 | + } |
| 632 | + BlockTree body = tree.getBody(); |
| 633 | + if (body == null) { |
| 634 | + return; |
| 635 | + } |
| 636 | + boolean[] hasThrow = new boolean[] {false}; |
| 637 | + new TreeScanner<Void, Void>() { |
| 638 | + @Override |
| 639 | + public Void visitThrow(ThrowTree node, Void p) { |
| 640 | + hasThrow[0] = true; |
| 641 | + return super.visitThrow(node, p); |
| 642 | + } |
| 643 | + }.scan(body, null); |
| 644 | + if (!hasThrow[0]) { |
| 645 | + checker.reportError(tree, "if.null.throws.must.throw"); |
| 646 | + } |
| 647 | + } |
| 648 | + |
606 | 649 | @Override |
607 | 650 | public Void visitMethodInvocation(MethodInvocationTree tree, Void p) { |
608 | 651 | if (!permitClearProperty) { |
|
0 commit comments