in nullaway/src/main/java/com/uber/nullaway/handlers/contract/ContractCheckHandler.java [62:165]
public void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext) {
Symbol.MethodSymbol callee = ASTHelpers.getSymbol(tree);
Preconditions.checkNotNull(callee);
// Check to see if this method has an @Contract annotation
String contractString = ContractUtils.getContractString(callee, config);
if (contractString != null) {
// Found a contract, lets parse it.
String[] clauses = contractString.split(";");
if (clauses.length != 1) {
return;
}
String clause = clauses[0];
NullAway analysis = methodAnalysisContext.analysis();
VisitorState state = methodAnalysisContext.state();
String[] antecedent =
getAntecedent(clause, tree, analysis, state, callee, tree.getParameters().size());
String consequent = getConsequent(clause, tree, analysis, state, callee);
boolean supported = true;
for (int i = 0; i < antecedent.length; ++i) {
String valueConstraint = antecedent[i].trim();
if (!(valueConstraint.equals("_")
|| valueConstraint.equals("!null")
|| valueConstraint.equals("null"))) {
supported = false;
}
}
if (!consequent.equals("!null")) {
supported = false;
}
if (!supported) {
return;
}
// we scan the method tree for the return nodes and check the contract
new TreePathScanner<Void, Void>() {
@Override
public Void visitReturn(ReturnTree returnTree, Void unused) {
VisitorState returnState = state.withPath(getCurrentPath());
Nullness nullness =
analysis
.getNullnessAnalysis(returnState)
.getNullnessForContractDataflow(
new TreePath(returnState.getPath(), returnTree.getExpression()),
returnState.context);
if (nullness == Nullness.NULLABLE || nullness == Nullness.NULL) {
String errorMessage;
// used for error message
int nonNullAntecedentCount = 0;
int nonNullAntecedentPosition = -1;
for (int i = 0; i < antecedent.length; ++i) {
String valueConstraint = antecedent[i].trim();
if (valueConstraint.equals("!null")) {
nonNullAntecedentCount += 1;
nonNullAntecedentPosition = i;
}
}
if (nonNullAntecedentCount == 1) {
errorMessage =
"Method "
+ callee.name
+ " has @Contract("
+ contractString
+ "), but this appears to be violated, as a @Nullable value may be returned when parameter "
+ tree.getParameters().get(nonNullAntecedentPosition).getName()
+ " is non-null.";
} else {
errorMessage =
"Method "
+ callee.name
+ " has @Contract("
+ contractString
+ "), but this appears to be violated, as a @Nullable value may be returned "
+ "when the contract preconditions are true.";
}
returnState.reportMatch(
analysis
.getErrorBuilder()
.createErrorDescription(
new ErrorMessage(
ErrorMessage.MessageTypes.ANNOTATION_VALUE_INVALID, errorMessage),
returnTree,
analysis.buildDescription(returnTree),
returnState,
null));
}
return super.visitReturn(returnTree, null);
}
}.scan(state.getPath(), null);
}
}