if (frame.getStackDepth() < 2) {
throw new DataflowAnalysisException("Stack underflow", methodGen, handle);
}
int numSlots = frame.getNumSlots();
Type lhsType_ = frame.getValue(numSlots - 2);
Type rhsType_ = frame.getValue(numSlots - 1);
// Ignore top and bottom values
if (lhsType_.getType() == T_TOP || lhsType_.getType() == T_BOTTOM || rhsType_.getType() == T_TOP
|| rhsType_.getType() == T_BOTTOM) {
return;
}
InvokeInstruction inv = (InvokeInstruction) handle.getInstruction();
MethodAnnotation calledMethodAnnotation = getMethodCalledAnnotation(cpg, inv);
boolean looksLikeTestCase = TestCaseDetector.likelyTestCase(XFactory.createXMethod(methodGen));
int priorityModifier = 0;
if (looksLikeTestCase) {
priorityModifier = 1;
}
if (rhsType_.getType() == T_NULL) {
// A literal null value was passed directly to equals().
if (!looksLikeTestCase) {
try {
IsNullValueDataflow isNullDataflow = classContext.getIsNullValueDataflow(method);
IsNullValueFrame isNullFrame = isNullDataflow.getFactAtLocation(location);
BugAnnotation a = BugInstance.getSourceForTopStackValue(classContext, method, location);
int priority = NORMAL_PRIORITY;
if (a instanceof FieldAnnotation && ((FieldAnnotation) a).isStatic()) {
priority = LOW_PRIORITY;
}
if (isNullFrame.isValid() && isNullFrame.getTopValue().isDefinitelyNull()) {
String type = "EC_NULL_ARG";
if (calledMethodAnnotation != null && calledMethodAnnotation.isStatic()){
type = "DMI_DOH";
priority = LOW_PRIORITY;
}
BugInstance bug = new BugInstance(this, type, priority + priorityModifier).addClassAndMethod(methodGen, sourceFile)
.addOptionalAnnotation(calledMethodAnnotation);
if (type.equals("DMI_DOH")) {
bug.addString("Use \"== null\" to check for a value being null");
}
bugAccumulator.accumulateBug(
bug,
SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile,
location.getHandle()));
}
} catch (CFGBuilderException e) {
AnalysisContext.logError("Error getting null value analysis", e);
}
}
return;
} else if (lhsType_.getType() == T_NULL) {
// Hmm...in this case, equals() is being invoked on
// a literal null value. This is really the
// purview of FindNullDeref. So, we'll just do nothing.
return;
} else if (!(lhsType_ instanceof ReferenceType) || !(rhsType_ instanceof ReferenceType)) {
bugReporter.logError("equals() used to compare non-object type(s) " + lhsType_ + " and " + rhsType_ + " in "
+ SignatureConverter.convertMethodSignature(methodGen) + " at " + location.getHandle());
return;
}
IncompatibleTypes result = IncompatibleTypes.getPriorityForAssumingCompatible(lhsType_, rhsType_);
if (lhsType_ instanceof ArrayType && rhsType_ instanceof ArrayType) {
String pattern = "EC_BAD_ARRAY_COMPARE";
IncompatibleTypes result2 = IncompatibleTypes.getPriorityForAssumingCompatible(lhsType_, rhsType_, true);
if (result2.getPriority() <= Priorities.NORMAL_PRIORITY) {
pattern = "EC_INCOMPATIBLE_ARRAY_COMPARE";
} else if (calledMethodAnnotation != null && calledMethodAnnotation.getClassName().equals("org.testng.Assert")) {
return;
}
bugAccumulator.accumulateBug(new BugInstance(this, pattern, NORMAL_PRIORITY).addClassAndMethod(methodGen, sourceFile)
.addFoundAndExpectedType(rhsType_, lhsType_)
.addSomeSourceForTopTwoStackValues(classContext, method, location)
.addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
return;
}
if (result.getPriority() >= Priorities.LOW_PRIORITY) {
addEqualsCheck(lhsType_.getSignature(), handle.getPosition());
addEqualsCheck(rhsType_.getSignature(), handle.getPosition());
}
if (result == IncompatibleTypes.SEEMS_OK) {
return;
}
if (result.getPriority() > Priorities.LOW_PRIORITY) {
return;
}
if (result == IncompatibleTypes.ARRAY_AND_NON_ARRAY || result == IncompatibleTypes.ARRAY_AND_OBJECT) {
String lhsSig = lhsType_.getSignature();
String rhsSig = rhsType_.getSignature();
boolean allOk = checkForWeirdEquals(lhsSig, rhsSig, new HashSet<XMethod>());
if (allOk) {
priorityModifier += 2;
}
bugAccumulator.accumulateBug(new BugInstance(this, "EC_ARRAY_AND_NONARRAY", result.getPriority() + priorityModifier)
.addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
.addSomeSourceForTopTwoStackValues(classContext, method, location)
.addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
} else if (result == IncompatibleTypes.INCOMPATIBLE_CLASSES) {
String lhsSig = lhsType_.getSignature();
String rhsSig = rhsType_.getSignature();
boolean core = lhsSig.startsWith("Ljava") && rhsSig.startsWith("Ljava");
if (core) {
looksLikeTestCase = false;
priorityModifier = 0;
}