edu.umd.cs.findbugs.detect.DontIgnoreResultOfPutIfAbsent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
The newest version!
/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2004-2006 University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.detect;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentMap;
import org.apache.bcel.Const;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.POP;
import org.apache.bcel.generic.Type;
import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugAnnotation;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.Priorities;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.TypeAnnotation;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilderException;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.Dataflow;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.LiveLocalStoreAnalysis;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.XClass;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.ba.type.TypeFrame;
import edu.umd.cs.findbugs.ba.vna.ValueNumber;
import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
import edu.umd.cs.findbugs.ba.vna.ValueNumberSourceInfo;
import edu.umd.cs.findbugs.bcel.BCELUtil;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
import edu.umd.cs.findbugs.util.ClassName;
public class DontIgnoreResultOfPutIfAbsent implements Detector {
final BugReporter bugReporter;
final BugAccumulator accumulator;
final ClassDescriptor concurrentMapDescriptor = DescriptorFactory.createClassDescriptor(ConcurrentMap.class);
public DontIgnoreResultOfPutIfAbsent(BugReporter bugReporter) {
this.bugReporter = bugReporter;
this.accumulator = new BugAccumulator(bugReporter);
}
@Override
public void report() {
// nothing to do?
}
@Override
public void visitClassContext(ClassContext classContext) {
JavaClass javaClass = classContext.getJavaClass();
ConstantPool pool = javaClass.getConstantPool();
boolean found = false;
for (Constant constantEntry : pool.getConstantPool()) {
if (constantEntry instanceof ConstantNameAndType) {
ConstantNameAndType nt = (ConstantNameAndType) constantEntry;
if ("putIfAbsent".equals(nt.getName(pool))) {
found = true;
break;
}
}
}
if (!found) {
return;
}
Method[] methodList = javaClass.getMethods();
for (Method method : methodList) {
MethodGen methodGen = classContext.getMethodGen(method);
if (methodGen == null) {
continue;
}
try {
analyzeMethod(classContext, method);
} catch (DataflowAnalysisException e) {
bugReporter.logError("Error analyzing " + method.toString(), e);
} catch (CFGBuilderException e) {
bugReporter.logError("Error analyzing " + method.toString(), e);
}
}
}
final static boolean DEBUG = false;
@edu.umd.cs.findbugs.internalAnnotations.StaticConstant
static HashSet immutableClassNames = new HashSet<>();
static {
immutableClassNames.add("java/lang/Integer");
immutableClassNames.add("java/lang/Long");
immutableClassNames.add("java/lang/String");
immutableClassNames.add("java/util/Comparator");
}
private static int getPriorityForBeingMutable(Type type) {
if (type instanceof ArrayType) {
return HIGH_PRIORITY;
} else if (type instanceof ObjectType) {
UnreadFieldsData unreadFields = AnalysisContext.currentAnalysisContext().getUnreadFieldsData();
ClassDescriptor cd = DescriptorFactory.getClassDescriptor((ObjectType) type);
@SlashedClassName
String className = cd.getClassName();
if (immutableClassNames.contains(className)) {
return Priorities.LOW_PRIORITY;
}
XClass xClass = AnalysisContext.currentXFactory().getXClass(cd);
if (xClass == null) {
return Priorities.IGNORE_PRIORITY;
}
ClassDescriptor superclassDescriptor = xClass.getSuperclassDescriptor();
if (superclassDescriptor != null) {
@SlashedClassName
String superClassName = superclassDescriptor.getClassName();
if ("java/lang/Enum".equals(superClassName)) {
return Priorities.LOW_PRIORITY;
}
}
boolean hasMutableField = false;
boolean hasUpdates = false;
for (XField f : xClass.getXFields()) {
if (!f.isStatic()) {
if (!f.isFinal() && !f.isSynthetic()) {
hasMutableField = true;
if (unreadFields.isWrittenOutsideOfInitialization(f)) {
hasUpdates = true;
}
}
String signature = f.getSignature();
if (signature.startsWith("Ljava/util/concurrent") || signature.startsWith("Ljava/lang/StringB")
|| signature.charAt(0) == '[' || signature.indexOf("Map") >= 0 || signature.indexOf("List") >= 0
|| signature.indexOf("Set") >= 0) {
hasMutableField = hasUpdates = true;
}
}
}
if (!hasMutableField && !xClass.isInterface() && !xClass.isAbstract()) {
return Priorities.LOW_PRIORITY;
}
if (hasUpdates || className.startsWith("java/util") || className.indexOf("Map") >= 0
|| className.indexOf("List") >= 0) {
return Priorities.HIGH_PRIORITY;
}
return Priorities.NORMAL_PRIORITY;
} else {
return Priorities.IGNORE_PRIORITY;
}
}
private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException {
if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Const.ACC_BRIDGE) == Const.ACC_BRIDGE) {
return;
}
if (DEBUG) {
System.out.println(" Analyzing method " + classContext.getJavaClass().getClassName() + "." + method.getName());
}
JavaClass javaClass = classContext.getJavaClass();
ConstantPoolGen cpg = classContext.getConstantPoolGen();
Dataflow llsaDataflow = classContext.getLiveLocalStoreDataflow(method);
MethodGen methodGen = classContext.getMethodGen(method);
CFG cfg = classContext.getCFG(method);
ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method);
TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
String sourceFileName = javaClass.getSourceFileName();
for (Iterator i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
InstructionHandle handle = location.getHandle();
Instruction ins = handle.getInstruction();
if (ins instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) ins;
if ("putIfAbsent".equals(invoke.getMethodName(cpg))) {
String signature = invoke.getSignature(cpg);
if ("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;".equals(signature)
&& !(invoke instanceof INVOKESTATIC)) {
TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
Type objType = typeFrame.getStackValue(2);
if (extendsConcurrentMap(ClassName.fromFieldSignatureToDottedClassName(objType.getSignature()))) {
InstructionHandle next = handle.getNext();
boolean isIgnored = next != null && next.getInstruction() instanceof POP;
// boolean isImmediateNullTest = next != null
// && (next.getInstruction() instanceof IFNULL || next.getInstruction() instanceof IFNONNULL);
if (isIgnored) {
BitSet live = llsaDataflow.getAnalysis().getFactAtLocation(location);
ValueNumberFrame vna = vnaDataflow.getAnalysis().getFactAtLocation(location);
ValueNumber vn = vna.getTopValue();
int locals = vna.getNumLocals();
// boolean isRetained = false;
for (int pos = 0; pos < locals; pos++) {
if (vna.getValue(pos).equals(vn) && live.get(pos)) {
BugAnnotation ba = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, vn,
vnaDataflow.getFactAtLocation(location), "VALUE_OF");
if (ba == null) {
continue;
}
String pattern = "RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED";
Type type = typeFrame.getTopValue();
int priority = getPriorityForBeingMutable(type);
BugInstance bugInstance = new BugInstance(this, pattern, priority)
.addClassAndMethod(methodGen, sourceFileName).addCalledMethod(methodGen, invoke)
.add(new TypeAnnotation(type)).add(ba);
SourceLineAnnotation where = SourceLineAnnotation.fromVisitedInstruction(classContext,
method, location);
accumulator.accumulateBug(bugInstance, where);
// isRetained = true;
break;
}
}
}
}
}
}
}
}
accumulator.reportAccumulatedBugs();
}
private boolean extendsConcurrentMap(@DottedClassName String className) {
if ("java.util.concurrent.ConcurrentHashMap".equals(className)
|| className.equals(concurrentMapDescriptor.getDottedClassName())) {
return true;
}
ClassDescriptor c = DescriptorFactory.createClassDescriptorFromDottedClassName(className);
Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
try {
if (subtypes2.isSubtype(c, concurrentMapDescriptor)) {
return true;
}
} catch (ClassNotFoundException e) {
AnalysisContext.reportMissingClass(e);
}
return false;
}
}