/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2003-2008 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.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.meta.When;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.IfInstruction;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.ClassAnnotation;
import edu.umd.cs.findbugs.IntAnnotation;
import edu.umd.cs.findbugs.LocalVariableAnnotation;
import edu.umd.cs.findbugs.Priorities;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.StringAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.BasicBlock;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.DataflowCFGPrinter;
import edu.umd.cs.findbugs.ba.Edge;
import edu.umd.cs.findbugs.ba.EdgeTypes;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.ba.jsr305.Analysis;
import edu.umd.cs.findbugs.ba.jsr305.BackwardTypeQualifierDataflow;
import edu.umd.cs.findbugs.ba.jsr305.BackwardTypeQualifierDataflowAnalysis;
import edu.umd.cs.findbugs.ba.jsr305.BackwardTypeQualifierDataflowFactory;
import edu.umd.cs.findbugs.ba.jsr305.FlowValue;
import edu.umd.cs.findbugs.ba.jsr305.ForwardTypeQualifierDataflow;
import edu.umd.cs.findbugs.ba.jsr305.ForwardTypeQualifierDataflowAnalysis;
import edu.umd.cs.findbugs.ba.jsr305.ForwardTypeQualifierDataflowFactory;
import edu.umd.cs.findbugs.ba.jsr305.JSR305NullnessAnnotations;
import edu.umd.cs.findbugs.ba.jsr305.SourceSinkInfo;
import edu.umd.cs.findbugs.ba.jsr305.SourceSinkType;
import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation;
import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierApplications;
import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue;
import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValueSet;
import edu.umd.cs.findbugs.ba.vna.ValueNumber;
import edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysis;
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.CFGDetector;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.classfile.IAnalysisCache;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.classfile.MissingClassException;
import edu.umd.cs.findbugs.util.Util;
/**
* Check JSR-305 type qualifiers.
*
* @author David Hovemeyer
*/
public class CheckTypeQualifiers extends CFGDetector {
private static final boolean DEBUG = SystemProperties.getBoolean("ctq.debug");
private static final boolean DEBUG_DATAFLOW = SystemProperties.getBoolean("ctq.dataflow.debug");
private static final String DEBUG_DATAFLOW_MODE = SystemProperties.getProperty("ctq.dataflow.debug.mode", "both");
private static final String NONNULL_ANNOTATION = JSR305NullnessAnnotations.NONNULL.getClassName();
private static final String METHOD = SystemProperties.getProperty("ctq.method");
private final BugReporter bugReporter;
public CheckTypeQualifiers(BugReporter bugReporter) {
this.bugReporter = bugReporter;
}
boolean checked;
boolean shouldRunAnalysis;
/*
* (non-Javadoc)
*
* @see
* edu.umd.cs.findbugs.bcel.CFGDetector#visitClass(edu.umd.cs.findbugs.classfile
* .ClassDescriptor)
*/
@Override
public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
if (!checked) {
checked = true;
Collection<TypeQualifierValue<?>> allKnownTypeQualifiers = TypeQualifierValue.getAllKnownTypeQualifiers();
int size = allKnownTypeQualifiers.size();
if (size == 1) {
TypeQualifierValue<?> value = Util.first(allKnownTypeQualifiers);
if (!value.typeQualifier.getClassName().equals(NONNULL_ANNOTATION))
shouldRunAnalysis = true;
} else if (size > 1)
shouldRunAnalysis = true;
}
if (shouldRunAnalysis)
super.visitClass(classDescriptor);
}
/*
* (non-Javadoc)
*
* @see
* edu.umd.cs.findbugs.bcel.CFGDetector#visitMethodCFG(edu.umd.cs.findbugs
* .classfile.MethodDescriptor, edu.umd.cs.findbugs.ba.CFG)
*/
@Override
protected void visitMethodCFG(MethodDescriptor methodDescriptor, CFG cfg) throws CheckedAnalysisException {
if (METHOD != null && !methodDescriptor.getName().equals(METHOD)) {
return;
}
if (methodDescriptor.getName().startsWith("access$"))
return;
XMethod xMethod = XFactory.createXMethod(methodDescriptor);
if (DEBUG) {
System.out.println("CheckTypeQualifiers: checking " + methodDescriptor.toString());
}
Collection<TypeQualifierValue<?>> relevantQualifiers = Analysis.getRelevantTypeQualifiers(methodDescriptor, cfg);
for(Iterator<TypeQualifierValue<?>> i = relevantQualifiers.iterator(); i.hasNext(); ) {
if (i.next().getTypeQualifierClassDescriptor().getClassName().equals(NONNULL_ANNOTATION))
i.remove();
}
if (relevantQualifiers.isEmpty())
return;
if (DEBUG) {
System.out.println(" Relevant type qualifiers are " + relevantQualifiers);
}
IAnalysisCache analysisCache = Global.getAnalysisCache();
ForwardTypeQualifierDataflowFactory forwardDataflowFactory = analysisCache.getMethodAnalysis(
ForwardTypeQualifierDataflowFactory.class, methodDescriptor);
BackwardTypeQualifierDataflowFactory backwardDataflowFactory = analysisCache.getMethodAnalysis(
BackwardTypeQualifierDataflowFactory.class, methodDescriptor);
ValueNumberDataflow vnaDataflow = analysisCache.getMethodAnalysis(ValueNumberDataflow.class, methodDescriptor);
for (TypeQualifierValue typeQualifierValue : relevantQualifiers) {
try {
checkQualifier(xMethod, cfg, typeQualifierValue, forwardDataflowFactory, backwardDataflowFactory,
vnaDataflow);
} catch (MissingClassException e) {
AnalysisContext.reportMissingClass(e);
} catch (CheckedAnalysisException e) {
bugReporter.logError("Exception checking type qualifier " + typeQualifierValue.toString() + " on method "
+ methodDescriptor.toString(), e);
}
}
}
private String checkLocation;
/**
* Check a specific TypeQualifierValue on a method.
*
* @param xmethod
* MethodDescriptor of method
* @param cfg
* CFG of method
* @param typeQualifierValue
* TypeQualifierValue to check
* @param forwardDataflowFactory
* ForwardTypeQualifierDataflowFactory used to create forward
* dataflow analysis objects
* @param backwardDataflowFactory
* BackwardTypeQualifierDataflowFactory used to create backward
* dataflow analysis objects
* @param vnaDataflow
* ValueNumberDataflow for the method
*/
private void checkQualifier(XMethod xmethod, CFG cfg, TypeQualifierValue typeQualifierValue,
ForwardTypeQualifierDataflowFactory forwardDataflowFactory,
BackwardTypeQualifierDataflowFactory backwardDataflowFactory, ValueNumberDataflow vnaDataflow)
throws CheckedAnalysisException {
if (DEBUG) {
System.out.println("----------------------------------------------------------------------");
System.out.println("Checking type qualifier " + typeQualifierValue.toString() + " on method "
+ xmethod.toString());
if (typeQualifierValue.isStrictQualifier()) {
System.out.println(" Strict type qualifier");
}
System.out.println("----------------------------------------------------------------------");
}
if (DEBUG_DATAFLOW) {
System.out.println("********* Valuenumber analysis *********");
DataflowCFGPrinter<ValueNumberFrame, ValueNumberAnalysis> p = new DataflowCFGPrinter<ValueNumberFrame, ValueNumberAnalysis>(vnaDataflow);
p.print(System.out);
}
ForwardTypeQualifierDataflow forwardDataflow = forwardDataflowFactory.getDataflow(typeQualifierValue);
if (DEBUG_DATAFLOW && (DEBUG_DATAFLOW_MODE.startsWith("forward") || DEBUG_DATAFLOW_MODE.equals("both"))) {
System.out.println("********* Forwards analysis *********");
DataflowCFGPrinter<TypeQualifierValueSet, ForwardTypeQualifierDataflowAnalysis> p = new DataflowCFGPrinter<TypeQualifierValueSet, ForwardTypeQualifierDataflowAnalysis>(
forwardDataflow);
p.print(System.out);
}
BackwardTypeQualifierDataflow backwardDataflow = backwardDataflowFactory.getDataflow(typeQualifierValue);
if (DEBUG_DATAFLOW && (DEBUG_DATAFLOW_MODE.startsWith("backward") || DEBUG_DATAFLOW_MODE.equals("both"))) {
System.out.println("********* Backwards analysis *********");
DataflowCFGPrinter<TypeQualifierValueSet, BackwardTypeQualifierDataflowAnalysis> p = new DataflowCFGPrinter<TypeQualifierValueSet, BackwardTypeQualifierDataflowAnalysis>(
backwardDataflow);
p.print(System.out);
}
checkDataflow(xmethod, cfg, typeQualifierValue, vnaDataflow, forwardDataflow, backwardDataflow);
checkValueSources(xmethod, cfg, typeQualifierValue, vnaDataflow, forwardDataflow, backwardDataflow);
}
private void checkDataflow(XMethod xmethod, CFG cfg, TypeQualifierValue typeQualifierValue,
ValueNumberDataflow vnaDataflow, ForwardTypeQualifierDataflow forwardDataflow,
BackwardTypeQualifierDataflow backwardDataflow) throws DataflowAnalysisException, CheckedAnalysisException {
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location loc = i.next();
TypeQualifierValueSet forwardsFact = forwardDataflow.getFactAtLocation(loc);
TypeQualifierValueSet backwardsFact = backwardDataflow.getFactAfterLocation(loc);
if (!forwardsFact.isValid() || !backwardsFact.isValid()) {
continue;
}
if (DEBUG) {
checkLocation = "location " + loc.toCompactString();
}
checkForConflictingValues(xmethod, cfg, typeQualifierValue, forwardsFact, backwardsFact, loc,
loc, vnaDataflow.getFactAtLocation(loc));
checkForEqualityTest(xmethod, cfg, typeQualifierValue, forwardsFact, loc, vnaDataflow.getFactAtLocation(loc));
}
for (Iterator<Edge> i = cfg.edgeIterator(); i.hasNext();) {
Edge edge = i.next();
// NOTE: when checking forwards and backwards values on an edge,
// we don't want to apply BOTH edge transfer functions,
// since the purpose of the edge transfer function is to
// propagate information across phi nodes (effectively
// copying information about one value to another).
// Due to pruning of backwards values when a conflict is detected,
// we need to check backwards values as "early" as possible,
// meaning that we want to check at the edge target
// (before the backwards edge transfer function has pruned
// the backwards value.)
TypeQualifierValueSet forwardFact = forwardDataflow.getFactOnEdge(edge);
TypeQualifierValueSet backwardFact = backwardDataflow.getResultFact(edge.getTarget());
// The edge target location is where we can check
// for conflicting flow values.
Location edgeTargetLocation = getEdgeTargetLocation(cfg, edge);
ValueNumberFrame vnaFrame = (edgeTargetLocation != null) ? vnaDataflow.getFactAtLocation(edgeTargetLocation) : null;
// What location do we want to report to the user
// as where the conflict occurs?
// The edge source location is generally better,
// but edge target location is ok as a fallback.
Location locationToReport;
if (edge.getSource().getLastInstruction() != null) {
locationToReport = getEdgeSourceLocation(cfg, edge);
} else {
locationToReport = edgeTargetLocation;
}
checkForConflictingValues(xmethod, cfg, typeQualifierValue, forwardFact, backwardFact,
locationToReport, edgeTargetLocation, vnaFrame);
}
}
private void checkForEqualityTest(XMethod xmethod, CFG cfg, TypeQualifierValue typeQualifierValue,
TypeQualifierValueSet forwardsFact, Location loc, ValueNumberFrame factAtLocation) throws DataflowAnalysisException {
InstructionHandle handle = loc.getHandle();
Instruction ins = handle.getInstruction();
boolean isTest = false;
ConstantPoolGen cpg = cfg.getMethodGen().getConstantPool();
if (ins instanceof IfInstruction && ins.consumeStack(cpg) == 2) {
isTest = true;
} else if (ins instanceof InvokeInstruction && ins.consumeStack(cpg) == 2) {
InvokeInstruction invoke = (InvokeInstruction) ins;
isTest = invoke.getMethodName(cpg).equals("equals") &&invoke.getSignature(cpg).equals("(Ljava/lang/Object;)Z") ;
}
if (isTest) {
ValueNumber top = factAtLocation.getStackValue(0);
if (top.hasFlag(ValueNumber.CONSTANT_VALUE))
return;
ValueNumber next = factAtLocation.getStackValue(1);
if (next.hasFlag(ValueNumber.CONSTANT_VALUE))
return;
FlowValue topTQ = forwardsFact.getValue(top);
FlowValue nextTQ = forwardsFact.getValue(next);
if (DEBUG) {
System.out.println("Comparing values at " + loc.toCompactString());
System.out.println(" Comparing " + topTQ + " and " + nextTQ);
}
if (topTQ.equals(nextTQ)) return;
if (FlowValue.valuesConflict(typeQualifierValue.isStrictQualifier() && !xmethod.isIdentity(), topTQ, nextTQ)) {
BugInstance warning = new BugInstance(this,"TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS", HIGH_PRIORITY).addClassAndMethod(xmethod);
annotateWarningWithTypeQualifier(warning, typeQualifierValue);
for(SourceSinkInfo s : forwardsFact.getWhere(top))
annotateWarningWithSourceSinkInfo(warning, xmethod, top, s);
for(SourceSinkInfo s : forwardsFact.getWhere(next))
annotateWarningWithSourceSinkInfo(warning, xmethod, next, s);
SourceLineAnnotation observedLocation = SourceLineAnnotation.fromVisitedInstruction(xmethod.getMethodDescriptor(),
loc);
warning.add(observedLocation);
warning.addSomeSourceForTopTwoStackValues(classContext, method, loc);
bugReporter.reportBug(warning);
}
}
}
private void checkValueSources(XMethod xMethod, CFG cfg, TypeQualifierValue typeQualifierValue,
ValueNumberDataflow vnaDataflow, ForwardTypeQualifierDataflow forwardDataflow,
BackwardTypeQualifierDataflow backwardDataflow) throws DataflowAnalysisException, CheckedAnalysisException {
// Check to see if any backwards ALWAYS or NEVER values
// reach incompatible sources.
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
Set<SourceSinkInfo> sourceSet = forwardDataflow.getAnalysis().getSourceSinkInfoSet(location);
for (SourceSinkInfo source : sourceSet) {
ValueNumber vn = source.getValueNumber();
TypeQualifierValueSet backwardsFact = backwardDataflow.getFactAtLocation(location);
FlowValue backwardsFlowValue = backwardsFact.getValue(vn);
if (!(backwardsFlowValue == FlowValue.ALWAYS || backwardsFlowValue == FlowValue.NEVER)) {
continue;
}
if (DEBUG) {
System.out.println("Checking value source at " + location.toCompactString() + " for " + typeQualifierValue);
System.out.println(" back=" + backwardsFact);
System.out.println(" source=" + source);
}
// Check to see if this warning has already been reported
// because
// the dataflow values conflict directly with each other.
TypeQualifierValueSet forwardsFact = forwardDataflow.getFactAfterLocation(location);
FlowValue forwardsFlowValue = forwardsFact.getValue(vn);
if (FlowValue.valuesConflict(typeQualifierValue.isStrictQualifier() && !xMethod.isIdentity(), forwardsFlowValue, backwardsFlowValue)) {
continue;
}
if (FlowValue.backwardsValueConflictsWithSource(backwardsFlowValue, source, typeQualifierValue, xMethod.isIdentity())) {
String bugType = (backwardsFlowValue == FlowValue.NEVER) ? "TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK"
: "TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK";
emitSourceWarning(bugType, xMethod, typeQualifierValue, backwardsFlowValue, backwardsFact, source,
vn, location);
} else if (source.getWhen() == When.UNKNOWN && source.getType() == SourceSinkType.PARAMETER) {
int p = source.getParameter();
TypeQualifierAnnotation directTypeQualifierAnnotation = TypeQualifierApplications
.getDirectTypeQualifierAnnotation(xMethod, p, typeQualifierValue);
if (directTypeQualifierAnnotation != null && directTypeQualifierAnnotation.when == When.UNKNOWN) {
String bugType = (backwardsFlowValue == FlowValue.NEVER) ? "TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK"
: "TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK";
emitSourceWarning(bugType, xMethod, typeQualifierValue, backwardsFlowValue, backwardsFact,
source, vn, location);
}
} else if (source.getWhen() == When.UNKNOWN && typeQualifierValue.isStrictQualifier()) {
emitDataflowWarning(xMethod, typeQualifierValue, forwardsFact, backwardsFact, vn, forwardsFlowValue, backwardsFlowValue,
location, null, vnaDataflow.getFactAtLocation(location));
}
}
}
}
private Location getEdgeTargetLocation(CFG cfg, Edge edge) {
BasicBlock targetBlock = edge.getTarget();
// Target block is nonempty?
if (targetBlock.getFirstInstruction() != null) {
return new Location(targetBlock.getFirstInstruction(), targetBlock);
}
// Target block is an ETB?
if (targetBlock.isExceptionThrower()) {
BasicBlock fallThroughSuccessor = cfg.getSuccessorWithEdgeType(targetBlock, EdgeTypes.FALL_THROUGH_EDGE);
if (fallThroughSuccessor == null) {
// Fall through edge might have been pruned
for (Iterator<Edge> i = cfg.removedEdgeIterator(); i.hasNext();) {
Edge removedEdge = i.next();
if (removedEdge.getSource() == targetBlock && removedEdge.getType() == EdgeTypes.FALL_THROUGH_EDGE) {
fallThroughSuccessor = removedEdge.getTarget();
break;
}
}
}
if (fallThroughSuccessor != null && fallThroughSuccessor.getFirstInstruction() != null) {
return new Location(fallThroughSuccessor.getFirstInstruction(), fallThroughSuccessor);
}
}
return null;
}
private Location getEdgeSourceLocation(CFG cfg, Edge edge) {
BasicBlock sourceBlock = edge.getSource();
return (sourceBlock.getLastInstruction() != null) ? new Location(sourceBlock.getLastInstruction(), sourceBlock) : null;
}
private void checkForConflictingValues(XMethod xMethod, CFG cfg,
TypeQualifierValue typeQualifierValue, TypeQualifierValueSet forwardsFact, TypeQualifierValueSet backwardsFact,
Location locationToReport, Location locationWhereDoomedValueIsObserved, ValueNumberFrame vnaFrame) throws CheckedAnalysisException {
Set<ValueNumber> valueNumberSet = new HashSet<ValueNumber>();
valueNumberSet.addAll(forwardsFact.getValueNumbers());
valueNumberSet.addAll(backwardsFact.getValueNumbers());
for (ValueNumber vn : valueNumberSet) {
FlowValue forward = forwardsFact.getValue(vn);
FlowValue backward = backwardsFact.getValue(vn);
if (!FlowValue.valuesConflict(typeQualifierValue.isStrictQualifier() && !xMethod.isIdentity(), forward, backward))
continue;
if (DEBUG) {
System.out.println("Check " + vn + ": forward=" + forward + ", backward=" + backward + " at " + checkLocation);
forwardsFact.getValue(vn);
backwardsFact.getValue(vn);
}
emitDataflowWarning(xMethod, typeQualifierValue, forwardsFact, backwardsFact, vn, forward, backward,
locationToReport, locationWhereDoomedValueIsObserved, vnaFrame);
}
}
private void emitDataflowWarning(XMethod xMethod, TypeQualifierValue typeQualifierValue,
TypeQualifierValueSet forwardsFact, TypeQualifierValueSet backwardsFact, ValueNumber vn, FlowValue forward,
FlowValue backward, Location locationToReport, @CheckForNull Location locationWhereDoomedValueIsObserved, ValueNumberFrame vnaFrame)
throws CheckedAnalysisException {
String bugType;
if (typeQualifierValue.isStrictQualifier() && forward == FlowValue.UNKNOWN)
bugType = "TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED";
else if (backward == FlowValue.NEVER)
bugType = "TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED";
else
bugType = "TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED";
// Issue warning
BugInstance warning = new BugInstance(this, bugType, Priorities.NORMAL_PRIORITY).addClassAndMethod(xMethod);
annotateWarningWithTypeQualifier(warning, typeQualifierValue);
Set<? extends SourceSinkInfo> sourceSet = (forward == FlowValue.ALWAYS) ? forwardsFact.getWhereAlways(vn) : forwardsFact
.getWhereNever(vn);
for (SourceSinkInfo source : sourceSet) {
annotateWarningWithSourceSinkInfo(warning, xMethod, vn, source);
}
Set<? extends SourceSinkInfo> sinkSet = (backward == FlowValue.ALWAYS) ? backwardsFact.getWhereAlways(vn) : backwardsFact
.getWhereNever(vn);
Location sinkLocation = getSinkLocation(sinkSet);
if (sinkLocation == null) {
AnalysisContext.logError("Unable to compute sink location for " + xMethod);
return;
}
// Hopefully we can find the conflicted value in a local variable
if (locationWhereDoomedValueIsObserved != null) {
Method method = Global.getAnalysisCache().getMethodAnalysis(Method.class, xMethod.getMethodDescriptor());
LocalVariableAnnotation localVariable = ValueNumberSourceInfo.findLocalAnnotationFromValueNumber(method,
locationWhereDoomedValueIsObserved, vn, vnaFrame);
if (localVariable != null && !localVariable.equals(warning.getPrimaryLocalVariableAnnotation())) {
localVariable.setDescription(localVariable.isSignificant() ? "LOCAL_VARIABLE_VALUE_DOOMED_NAMED"
: "LOCAL_VARIABLE_VALUE_DOOMED");
warning.add(localVariable);
}
if (!sinkLocation.equals(locationToReport)) {
// Report where we observed the value.
// Note that for conflicts detected on control edges,
// we REPORT the edge source location
// rather than the target location, even though it is the
// target location where the conflict is detected.
// The only reason to use a different reporting location
// is to produce a more informative report for the user,
// since the edge source is where the branch is found.
SourceLineAnnotation observedLocation = SourceLineAnnotation.fromVisitedInstruction(xMethod.getMethodDescriptor(),
locationToReport);
observedLocation.setDescription("SOURCE_LINE_VALUE_DOOMED");
warning.add(observedLocation);
}
}
// Add value sinks
for (SourceSinkInfo sink : sinkSet) {
annotateWarningWithSourceSinkInfo(warning, xMethod, vn, sink);
}
bugReporter.reportBug(warning);
}
private void emitSourceWarning(String bugType, XMethod xMethod, TypeQualifierValue typeQualifierValue,
FlowValue backwardsFlowValue, TypeQualifierValueSet backwardsFact, SourceSinkInfo source, ValueNumber vn,
Location location) {
BugInstance warning = new BugInstance(this, bugType, Priorities.NORMAL_PRIORITY).addClassAndMethod(xMethod);
annotateWarningWithTypeQualifier(warning, typeQualifierValue);
annotateWarningWithSourceSinkInfo(warning, xMethod, vn, source);
Set<? extends SourceSinkInfo> sinkSet = (backwardsFlowValue == FlowValue.NEVER) ? backwardsFact.getWhereNever(vn) : backwardsFact
.getWhereAlways(vn);
for (SourceSinkInfo sink : sinkSet) {
annotateWarningWithSourceSinkInfo(warning, xMethod, vn, sink);
}
bugReporter.reportBug(warning);
}
private void annotateWarningWithTypeQualifier(BugInstance warning, TypeQualifierValue typeQualifierValue) {
if (TypeQualifierValue.hasMultipleVariants(typeQualifierValue)) {
StringBuilder buf = new StringBuilder();
buf.append("@");
buf.append(typeQualifierValue.typeQualifier.getDottedClassName());
// When there are multiple variants, qualify the type
// qualifier with the value indicating which variant.
buf.append("(");
buf.append(typeQualifierValue.value);
buf.append(")");
warning.addString(buf.toString()).describe(StringAnnotation.TYPE_QUALIFIER_ROLE);
} else {
warning.addClass(typeQualifierValue.typeQualifier).describe(ClassAnnotation.TYPE_QUALIFIER_ROLE);
}
}
private void annotateWarningWithSourceSinkInfo(BugInstance warning, XMethod xMethod, ValueNumber vn,
SourceSinkInfo sourceSinkInfo) {
MethodDescriptor methodDescriptor = xMethod.getMethodDescriptor();
switch (sourceSinkInfo.getType()) {
case PARAMETER:
try {
Method method = Global.getAnalysisCache().getMethodAnalysis(Method.class, methodDescriptor);
LocalVariableAnnotation lva = LocalVariableAnnotation.getParameterLocalVariableAnnotation(method,
sourceSinkInfo.getLocal());
lva.setDescription(lva.isSignificant() ? LocalVariableAnnotation.PARAMETER_VALUE_SOURCE_NAMED_ROLE
: LocalVariableAnnotation.PARAMETER_VALUE_SOURCE_ROLE);
warning.add(lva);
} catch (CheckedAnalysisException e) {
warning.addSourceLine(methodDescriptor, sourceSinkInfo.getLocation()).describe("SOURCE_LINE_VALUE_SOURCE");
}
break;
case CONSTANT_VALUE:
Object constantValue = sourceSinkInfo.getConstantValue();
if (constantValue instanceof String) {
warning.addString((String) constantValue).describe(StringAnnotation.STRING_CONSTANT_ROLE);
} else if (constantValue instanceof Integer) {
warning.addInt((Integer) constantValue).describe(IntAnnotation.INT_VALUE);
} else if (constantValue == null)
warning.addString("null").describe(StringAnnotation.STRING_NONSTRING_CONSTANT_ROLE);
else
warning.addString(constantValue.toString()).describe(StringAnnotation.STRING_NONSTRING_CONSTANT_ROLE);
break;
case RETURN_VALUE_OF_CALLED_METHOD:
case FIELD_LOAD:
warning.addSourceLine(methodDescriptor, sourceSinkInfo.getLocation()).describe("SOURCE_LINE_VALUE_SOURCE");
break;
case ARGUMENT_TO_CALLED_METHOD:
case RETURN_VALUE:
case FIELD_STORE:
warning.addSourceLine(methodDescriptor, sourceSinkInfo.getLocation());
return;
default:
throw new IllegalStateException();
}
}
private @CheckForNull
Location getSinkLocation(SourceSinkInfo sourceSinkInfo) {
switch (sourceSinkInfo.getType()) {
case ARGUMENT_TO_CALLED_METHOD:
case RETURN_VALUE:
case FIELD_STORE:
return sourceSinkInfo.getLocation();
default:
return null;
}
}
private @CheckForNull
Location getSinkLocation(Iterable<? extends SourceSinkInfo> info) {
for (SourceSinkInfo s : info) {
Location l = getSinkLocation(s);
if (l != null)
return l;
}
return null;
}
}