• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.uber.nullaway.handlers.contract;
2 
3 import com.google.common.base.Function;
4 import com.google.errorprone.VisitorState;
5 import com.sun.source.tree.Tree;
6 import com.sun.tools.javac.code.Symbol;
7 import com.uber.nullaway.Config;
8 import com.uber.nullaway.ErrorMessage;
9 import com.uber.nullaway.NullAway;
10 import com.uber.nullaway.NullabilityUtil;
11 import java.util.Set;
12 import java.util.stream.Collectors;
13 import javax.annotation.Nullable;
14 import javax.lang.model.element.AnnotationMirror;
15 import org.checkerframework.nullaway.javacutil.AnnotationUtils;
16 
17 /** An utility class for {@link ContractHandler} and {@link ContractCheckHandler}. */
18 public class ContractUtils {
19 
20   /**
21    * Returns a set of field names excluding their receivers (e.g. "this.a" will be "a")
22    *
23    * @param fieldNames A set of raw class field names.
24    * @return A set of trimmed field names.
25    */
trimReceivers(Set<String> fieldNames)26   public static Set<String> trimReceivers(Set<String> fieldNames) {
27     return fieldNames
28         .stream()
29         .map((Function<String, String>) input -> input.substring(input.lastIndexOf(".") + 1))
30         .collect(Collectors.toSet());
31   }
32 
33   /**
34    * Parses the contract clause and returns the consequent in the contract.
35    *
36    * @param clause The contract clause.
37    * @param tree The AST Node for contract.
38    * @param analysis A reference to the running NullAway analysis.
39    * @param state The current visitor state.
40    * @param callee Symbol for callee.
41    * @return consequent in the contract.
42    */
getConsequent( String clause, Tree tree, NullAway analysis, VisitorState state, Symbol callee)43   static String getConsequent(
44       String clause, Tree tree, NullAway analysis, VisitorState state, Symbol callee) {
45 
46     String[] parts = clause.split("->");
47     if (parts.length != 2) {
48       String message =
49           "Invalid @Contract annotation detected for method "
50               + callee
51               + ". It contains the following uparseable clause: "
52               + clause
53               + "(see https://www.jetbrains.com/help/idea/contract-annotations.html).";
54       state.reportMatch(
55           analysis
56               .getErrorBuilder()
57               .createErrorDescription(
58                   new ErrorMessage(ErrorMessage.MessageTypes.ANNOTATION_VALUE_INVALID, message),
59                   tree,
60                   analysis.buildDescription(tree),
61                   state));
62     }
63     return parts[1].trim();
64   }
65 
66   /**
67    * Parses the contract clause and returns the antecedents in the contract.
68    *
69    * @param clause The contract clause.
70    * @param tree The AST Node for contract.
71    * @param analysis A reference to the running NullAway analysis.
72    * @param state The current visitor state.
73    * @param callee Symbol for callee.
74    * @param numOfArguments Number of arguments in the method associated with the contract.
75    * @return antecedents in the contract.
76    */
getAntecedent( String clause, Tree tree, NullAway analysis, VisitorState state, Symbol callee, int numOfArguments)77   static String[] getAntecedent(
78       String clause,
79       Tree tree,
80       NullAway analysis,
81       VisitorState state,
82       Symbol callee,
83       int numOfArguments) {
84 
85     String[] parts = clause.split("->");
86 
87     String[] antecedent = parts[0].split(",");
88 
89     if (antecedent.length != numOfArguments) {
90       String message =
91           "Invalid @Contract annotation detected for method "
92               + callee
93               + ". It contains the following uparseable clause: "
94               + clause
95               + " (incorrect number of arguments in the clause's antecedent ["
96               + antecedent.length
97               + "], should be the same as the number of "
98               + "arguments in for the method ["
99               + numOfArguments
100               + "]).";
101       state.reportMatch(
102           analysis
103               .getErrorBuilder()
104               .createErrorDescription(
105                   new ErrorMessage(ErrorMessage.MessageTypes.ANNOTATION_VALUE_INVALID, message),
106                   tree,
107                   analysis.buildDescription(tree),
108                   state));
109     }
110     return antecedent;
111   }
112 
113   /**
114    * Returns the value of a Contract annotation if present on the method.
115    *
116    * @param methodSymbol the method to check for a Contract annotation
117    * @param config the NullAway config
118    * @return the value of a Contract annotation if present, or {@code null} if not present.
119    */
120   @Nullable
getContractString(Symbol.MethodSymbol methodSymbol, Config config)121   static String getContractString(Symbol.MethodSymbol methodSymbol, Config config) {
122     for (AnnotationMirror annotation : methodSymbol.getAnnotationMirrors()) {
123       String name = AnnotationUtils.annotationName(annotation);
124       if (config.isContractAnnotation(name)) {
125         return NullabilityUtil.getAnnotationValue(methodSymbol, name);
126       }
127     }
128     return null;
129   }
130 }
131