All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.errorprone.bugpatterns.SelfAssertion Maven / Gradle / Ivy
/*
* Copyright 2016 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone.bugpatterns;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.matchers.Description.NO_MATCH;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import static com.google.errorprone.util.ASTHelpers.getReceiver;
import static com.google.errorprone.util.ASTHelpers.getSymbol;
import static com.google.errorprone.util.ASTHelpers.getType;
import static com.google.errorprone.util.ASTHelpers.isSameType;
import static com.google.errorprone.util.ASTHelpers.sameVariable;
import static java.lang.String.format;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.bugpatterns.threadsafety.ConstantExpressions;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import javax.inject.Inject;
/** A {@link BugPattern}; see the summary. */
@BugPattern(
summary = "This assertion will always fail or succeed.",
altNames = "TruthSelfEquals",
severity = ERROR)
public final class SelfAssertion extends BugChecker implements MethodInvocationTreeMatcher {
private final Matcher equalsMatcher =
anyOf(
allOf(
instanceMethod()
.anyClass()
.namedAnyOf(
"isEqualTo",
"isSameInstanceAs",
"containsExactlyElementsIn",
"containsAtLeastElementsIn",
"areEqualTo"),
this::truthSameArguments),
allOf(
staticMethod()
.onClassAny(
"org.junit.Assert", "junit.framework.Assert", "junit.framework.TestCase")
.namedAnyOf("assertEquals", "assertArrayEquals"),
this::junitSameArguments));
private final Matcher notEqualsMatcher =
anyOf(
allOf(
instanceMethod().anyClass().namedAnyOf("isNotEqualTo", "isNotSameInstanceAs"),
this::truthSameArguments),
allOf(
staticMethod()
.onClassAny(
"org.junit.Assert", "junit.framework.Assert", "junit.framework.TestCase")
.namedAnyOf("assertNotEquals"),
this::junitSameArguments));
private static final Matcher ASSERT_THAT =
anyOf(
staticMethod().anyClass().named("assertThat"),
instanceMethod().onDescendantOf("com.google.common.truth.TestVerb").named("that"),
instanceMethod()
.onDescendantOf("com.google.common.truth.StandardSubjectBuilder")
.named("that"));
private final ConstantExpressions constantExpressions;
@Inject
SelfAssertion(ConstantExpressions constantExpressions) {
this.constantExpressions = constantExpressions;
}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (tree.getArguments().isEmpty()) {
return NO_MATCH;
}
if (equalsMatcher.matches(tree, state)) {
return buildDescription(tree)
.setMessage(generateSummary(getSymbol(tree).getSimpleName().toString(), "passes"))
.build();
}
if (notEqualsMatcher.matches(tree, state)) {
return buildDescription(tree)
.setMessage(generateSummary(getSymbol(tree).getSimpleName().toString(), "fails"))
.build();
}
return NO_MATCH;
}
private static String generateSummary(String methodName, String constantOutput) {
return format(
"You are passing identical arguments to the %s method, so this assertion always %s. THIS IS"
+ " LIKELY A BUG! If you are trying to test the correctness of an equals()"
+ " implementation, use EqualsTester instead.",
methodName, constantOutput);
}
private boolean junitSameArguments(MethodInvocationTree tree, VisitorState state) {
var arguments = tree.getArguments();
if (arguments.isEmpty()) {
return false;
}
var firstArgument = tree.getArguments().get(0);
ExpressionTree expected;
ExpressionTree actual;
if (tree.getArguments().size() > 2
&& isSameType(getType(firstArgument), state.getSymtab().stringType, state)) {
expected = arguments.get(1);
actual = arguments.get(2);
} else {
expected = arguments.get(0);
actual = arguments.get(1);
}
return sameExpression(state, expected, actual);
}
private boolean truthSameArguments(MethodInvocationTree tree, VisitorState state) {
ExpressionTree rec = getReceiver(tree);
if (rec == null) {
return false;
}
if (!ASSERT_THAT.matches(rec, state)) {
return false;
}
if (((MethodInvocationTree) rec).getArguments().size() != 1
|| tree.getArguments().size() != 1) {
// Oops: we over-matched and this doesn't look like a normal assertion.
return false;
}
ExpressionTree receiverExpression = getOnlyElement(((MethodInvocationTree) rec).getArguments());
ExpressionTree invocationExpression = getOnlyElement(tree.getArguments());
return sameExpression(state, receiverExpression, invocationExpression);
}
private boolean sameExpression(
VisitorState state, ExpressionTree receiverExpression, ExpressionTree invocationExpression) {
if (sameVariable(receiverExpression, invocationExpression)) {
return true;
}
var receiverConstant = constantExpressions.constantExpression(receiverExpression, state);
var invocationConstant = constantExpressions.constantExpression(invocationExpression, state);
return receiverConstant.isPresent() && receiverConstant.equals(invocationConstant);
}
}