
jp.skypencil.findbugs.slf4j.parameter.AbstractDetectorForParameterArray Maven / Gradle / Ivy
package jp.skypencil.findbugs.slf4j.parameter;
import static org.apache.bcel.Const.INVOKEINTERFACE;
import static org.apache.bcel.Const.INVOKESTATIC;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.OpcodeStack.Item;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
import edu.umd.cs.findbugs.internalAnnotations.StaticConstant;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.Type;
/**
* Basic detector to support detection about parameter array.
*
* @author Kengo TODA
*/
public abstract class AbstractDetectorForParameterArray extends OpcodeStackDetector {
@StaticConstant
private static final ImmutableSet TARGET_METHOD_NAMES =
ImmutableSet.of("trace", "debug", "info", "warn", "error");
@StaticConstant
// these methods do not use formatter
private static final ImmutableSet SIGS_WITHOUT_FORMAT =
ImmutableSet.of(
"(Ljava/lang/String;)V",
"(Lorg/slf4j/Maker;Ljava/lang/String;)V",
"(Ljava/lang/String;Ljava/lang/Throwable;)V",
"(Lorg/slf4j/Maker;Ljava/lang/String;Ljava/lang/Throwable;)V");
@StaticConstant
private static final Pattern SIGNATURE_PATTERN = Pattern.compile("^\\((.*)\\).*$");
private Table> potentialBugs;
private Multimap calledWithNonConstants;
private Table> calledWithConstants;
private final BugReporter bugReporter;
private final ThrowableHandler throwableHandler;
private final ArrayDataHandler arrayDataHandler;
public AbstractDetectorForParameterArray(BugReporter bugReporter) {
this.bugReporter = bugReporter;
this.throwableHandler = new ThrowableHandler();
this.arrayDataHandler = new ArrayDataHandler(createArrayCheckStrategy());
}
protected abstract ArrayDataHandler.Strategy createArrayCheckStrategy();
@Override
public void visitClassContext(ClassContext classContext) {
potentialBugs = HashBasedTable.create();
calledWithNonConstants = ArrayListMultimap.create();
calledWithConstants = HashBasedTable.create();
super.visitClassContext(classContext);
validatePrivateMethodCall();
}
/**
* Check all private method invocation, to detect {@code SLF4J_FORMAT_SHOULD_BE_CONST}.
*
* @see https://github.com/KengoTODA/findbugs-slf4j/issues/35
*/
private void validatePrivateMethodCall() {
for (Cell> cell : potentialBugs.cellSet()) {
Method method = cell.getRowKey();
String methodSignature = method.getName() + method.getSignature();
if (calledWithNonConstants.containsEntry(methodSignature, cell.getColumnKey())) {
for (BugInstance bug : cell.getValue()) {
bugReporter.reportBug(bug);
}
}
}
}
@Override
public final void sawOpcode(int seen) {
try {
checkEvents(seen);
} finally {
arrayDataHandler.sawOpcode(getStack(), seen);
}
}
private void checkEvents(int seen) {
if (seen == INVOKEINTERFACE) {
if (!Objects.equal("org/slf4j/Logger", getClassConstantOperand())
|| !TARGET_METHOD_NAMES.contains(getNameConstantOperand())) {
return;
}
String signature = getSigConstantOperand();
String formatString = findFormatString(signature);
ArrayData arrayData = findArrayData(signature);
onLog(formatString, arrayData);
} else if (isMethodCall()) {
if (Objects.equal(getClassDescriptor().getClassName(), getClassConstantOperand())) {
// calling methods in the same class
String methodSignature = getNameConstantOperand() + getSigConstantOperand();
int arguments = Type.getArgumentTypes(getSigConstantOperand()).length;
int shift = seen == INVOKESTATIC ? 0 : 1;
for (int i = shift; i < arguments + shift; ++i) {
Item item = getStack().getStackItem(i);
if (item.getConstant() == null) {
calledWithNonConstants.put(methodSignature, Integer.valueOf(i));
} else {
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy