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