/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2004,2005 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.Set;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ACONST_NULL;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ANEWARRAY;
import org.apache.bcel.generic.ASTORE;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.ConstantPushInstruction;
import org.apache.bcel.generic.DUP;
import org.apache.bcel.generic.DUP2;
import org.apache.bcel.generic.GETFIELD;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.IINC;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.IndexedInstruction;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.LDC;
import org.apache.bcel.generic.LRETURN;
import org.apache.bcel.generic.LSTORE;
import org.apache.bcel.generic.LoadInstruction;
import org.apache.bcel.generic.MULTIANEWARRAY;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.NEWARRAY;
import org.apache.bcel.generic.StoreInstruction;
import org.apache.bcel.generic.Type;

import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.FieldAnnotation;
import edu.umd.cs.findbugs.FindBugsAnalysisFeatures;
import edu.umd.cs.findbugs.LocalVariableAnnotation;
import edu.umd.cs.findbugs.Priorities;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
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.ch.Subtypes2;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.ba.type.TypeFrame;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.props.WarningProperty;
import edu.umd.cs.findbugs.props.WarningPropertySet;
import edu.umd.cs.findbugs.props.WarningPropertyUtil;
import edu.umd.cs.findbugs.util.ClassName;
import edu.umd.cs.findbugs.visitclass.PreorderVisitor;

/**
 * Find dead stores to local variables.
 * 
 * @author David Hovemeyer
 * @author Bill Pugh
 */
public class FindDeadLocalStores implements Detector {

    private static final boolean DEBUG = SystemProperties.getBoolean("fdls.debug");

    // Define the name of the property that is used to exclude named local
    // variables
    // from Dead Local Storage detection...
    private static final String FINDBUGS_EXCLUDED_LOCALS_PROP_NAME = "findbugs.dls.exclusions";

    // Define a collection of excluded local variables...
    private static final Set<String> EXCLUDED_LOCALS = new HashSet<String>();

    private static final boolean DO_EXCLUDE_LOCALS = SystemProperties.getProperty(FINDBUGS_EXCLUDED_LOCALS_PROP_NAME) != null;
    static {
        EXCLUDED_LOCALS.add("gxp_locale");
        // Get the value of the property...
        String exclLocalsProperty = SystemProperties.getProperty(FINDBUGS_EXCLUDED_LOCALS_PROP_NAME);

        // If we have one, then split its contents into a list...
        if (exclLocalsProperty != null) {
            for (String s : exclLocalsProperty.split(",")) {
                String s2 = s.trim();
                if (s2.length() > 0)
                    EXCLUDED_LOCALS.add(s2);
            }
        }
    }

    /**
     * System property to enable a feature that suppresses warnings if there is
     * at least one live store on the line where the warning would be reported.
     * Eliminates some FPs due to inlining/duplication of finally blocks. But,
     * kills some legitimate warnings where there are truly multiple stores on
     * the same line.
     */
    private static final boolean SUPPRESS_IF_AT_LEAST_ONE_LIVE_STORE_ON_LINE = SystemProperties
            .getBoolean("findbugs.dls.suppressIfOneLiveStore");

    // private static final Set<String> classesAlreadyReportedOn = new
    // HashSet<String>();
    /**
     * Opcodes of instructions that load constant values that often indicate
     * defensive programming.
     */
    private static final BitSet defensiveConstantValueOpcodes = new BitSet();
    static {
        defensiveConstantValueOpcodes.set(Constants.DCONST_0);
        defensiveConstantValueOpcodes.set(Constants.DCONST_1);
        defensiveConstantValueOpcodes.set(Constants.FCONST_0);
        defensiveConstantValueOpcodes.set(Constants.FCONST_1);
        defensiveConstantValueOpcodes.set(Constants.ACONST_NULL);
        defensiveConstantValueOpcodes.set(Constants.ICONST_M1);
        defensiveConstantValueOpcodes.set(Constants.ICONST_0);
        defensiveConstantValueOpcodes.set(Constants.ICONST_1);
        defensiveConstantValueOpcodes.set(Constants.ICONST_2);
        defensiveConstantValueOpcodes.set(Constants.ICONST_3);
        defensiveConstantValueOpcodes.set(Constants.ICONST_4);
        defensiveConstantValueOpcodes.set(Constants.ICONST_5);
        defensiveConstantValueOpcodes.set(Constants.LCONST_0);
        defensiveConstantValueOpcodes.set(Constants.LCONST_1);
        defensiveConstantValueOpcodes.set(Constants.LDC);
        defensiveConstantValueOpcodes.set(Constants.LDC_W);
        defensiveConstantValueOpcodes.set(Constants.LDC2_W);
    }

    private BugReporter bugReporter;

    public FindDeadLocalStores(BugReporter bugReporter) {
        this.bugReporter = bugReporter;
        if (DEBUG)
            System.out.println("Debugging FindDeadLocalStores detector");
    }

    private boolean prescreen(ClassContext classContext, Method method) {
        return true;
    }

    public void visitClassContext(ClassContext classContext) {
        JavaClass javaClass = classContext.getJavaClass();

        Method[] methodList = javaClass.getMethods();

        for (Method method : methodList) {
            MethodGen methodGen = classContext.getMethodGen(method);
            if (methodGen == null)
                continue;

            if (!prescreen(classContext, method))
                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);
            }
        }
    }

    private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException {
        if (method.isSynthetic() || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE)
            return;

        if (DEBUG) {
            System.out.println("    Analyzing method " + classContext.getJavaClass().getClassName() + "." + method.getName());
        }

        JavaClass javaClass = classContext.getJavaClass();
        BitSet linesMentionedMultipleTimes = classContext.linesMentionedMultipleTimes(method);
        BugAccumulator accumulator = new BugAccumulator(bugReporter);
        Dataflow<BitSet, LiveLocalStoreAnalysis> llsaDataflow = classContext.getLiveLocalStoreDataflow(method);

        int numLocals = method.getCode().getMaxLocals();
        int[] localStoreCount = new int[numLocals];
        int[] localLoadCount = new int[numLocals];
        int[] localIncrementCount = new int[numLocals];
        MethodGen methodGen = classContext.getMethodGen(method);
        CFG cfg = classContext.getCFG(method);
        if (cfg.isFlagSet(CFG.FOUND_INEXACT_UNCONDITIONAL_THROWERS))
            return;
        BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry());
        BitSet complainedAbout = new BitSet();
        TypeDataflow typeDataflow = classContext.getTypeDataflow(method);

        // Get number of locals that are parameters.
        int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
        if (!method.isStatic())
            localsThatAreParameters++;

        // Scan method to determine number of loads, stores, and increments
        // of local variables.
        countLocalStoresLoadsAndIncrements(localStoreCount, localLoadCount, localIncrementCount, cfg);
        for (int i = 0; i < localsThatAreParameters; i++)
            localStoreCount[i]++;

        // For each source line, keep track of # times
        // the line was a live store. This can eliminate false positives
        // due to inlining of finally blocks.
        BitSet liveStoreSourceLineSet = new BitSet();

        // Scan method for
        // - dead stores
        // - stores to parameters that are dead upon entry to the method
        for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
            Location location = i.next();

            BugInstance pendingBugReportAboutOverwrittenParameter = null;
            try {
                WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
                // Skip any instruction which is not a store
                if (!isStore(location))
                    continue;

                // Heuristic: exception handler blocks often contain
                // dead stores generated by the compiler.
                if (location.getBasicBlock().isExceptionHandler())
                    propertySet.addProperty(DeadLocalStoreProperty.EXCEPTION_HANDLER);
                InstructionHandle handle = location.getHandle();
                int pc = handle.getPosition();
                IndexedInstruction ins = (IndexedInstruction) location.getHandle().getInstruction();

                int local = ins.getIndex();

                // Get live stores at this instruction.
                // Note that the analysis also computes which stores were
                // killed by a subsequent unconditional store.
                BitSet liveStoreSet = llsaDataflow.getAnalysis().getFactAtLocation(location);

                // Is store alive?
                boolean storeLive = llsaDataflow.getAnalysis().isStoreAlive(liveStoreSet, local);

                LocalVariableAnnotation lvAnnotation = LocalVariableAnnotation.getLocalVariableAnnotation(method, location, ins);

                String sourceFileName = javaClass.getSourceFileName();
                if (lvAnnotation.getName().equals("?")) {
                    if (sourceFileName.endsWith(".groovy"))
                        continue;
                    if (method.getCode().getLocalVariableTable() != null)
                        continue;
                }

                SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
                        sourceFileName, location.getHandle());

                if (DEBUG) {
                    System.out.println("    Store at " + sourceLineAnnotation.getStartLine() + "@"
                            + location.getHandle().getPosition() + " is " + (storeLive ? "live" : "dead"));
                    System.out.println("Previous is: " + location.getHandle().getPrev());
                }

                // Note source lines of live stores.
                if (storeLive && sourceLineAnnotation.getStartLine() > 0) {
                    liveStoreSourceLineSet.set(sourceLineAnnotation.getStartLine());
                }

                String lvName = lvAnnotation.getName();
                if (lvName.charAt(0) == '$' || lvName.charAt(0) == '_')
                    propertySet.addProperty(DeadLocalStoreProperty.SYNTHETIC_NAME);
                if (EXCLUDED_LOCALS.contains(lvName))
                    continue;
                propertySet.setProperty(DeadLocalStoreProperty.LOCAL_NAME, lvName);

                boolean isParameter = local < localsThatAreParameters;
                if (isParameter)
                    propertySet.addProperty(DeadLocalStoreProperty.IS_PARAMETER);

                Field shadowedField = null;

                for (Field f : javaClass.getFields()) {
                    if (f.getName().equals(lvName) && f.isStatic() == method.isStatic()) {
                        shadowedField = f;
                        propertySet.addProperty(DeadLocalStoreProperty.SHADOWS_FIELD);
                        break;
                    }
                }

                // Is this a store to a parameter which was dead on entry to the
                // method?
                boolean parameterThatIsDeadAtEntry = isParameter
                        && !llsaDataflow.getAnalysis().isStoreAlive(liveStoreSetAtEntry, local);
                if (parameterThatIsDeadAtEntry && !complainedAbout.get(local)) {

                    int priority = storeLive ? LOW_PRIORITY : NORMAL_PRIORITY;
                    if (shadowedField != null)
                        priority--;
                    pendingBugReportAboutOverwrittenParameter = new BugInstance(this, "IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN",
                            priority).addClassAndMethod(methodGen, sourceFileName).add(lvAnnotation);

                    if (shadowedField != null)
                        pendingBugReportAboutOverwrittenParameter.addField(
                                FieldAnnotation.fromBCELField(classContext.getJavaClass(), shadowedField)).describe(
                                FieldAnnotation.DID_YOU_MEAN_ROLE);

                    pendingBugReportAboutOverwrittenParameter.addSourceLine(classContext, methodGen, sourceFileName,
                            location.getHandle());
                    complainedAbout.set(local);
                }

                if (storeLive)
                    continue;

                TypeFrame typeFrame = typeDataflow.getAnalysis().getFactAtLocation(location);
                Type typeOfValue = null;
                if (typeFrame.isValid() && typeFrame.getStackDepth() > 0) {
                    typeOfValue = typeFrame.getTopValue();
                }

                boolean storeOfNull = false;
                InstructionHandle prevInsHandle = location.getHandle().getPrev();
                if (prevInsHandle != null) {
                    Instruction prevIns = prevInsHandle.getInstruction();
                    boolean foundDeadClassInitialization = false;
                    String initializationOf = null;
                    if (prevIns instanceof ConstantPushInstruction)
                        continue; // not an interesting dead store
                    else if (prevIns instanceof GETSTATIC) {
                        GETSTATIC getStatic = (GETSTATIC) prevIns;
                        ConstantPoolGen cpg = methodGen.getConstantPool();
                        foundDeadClassInitialization = getStatic.getFieldName(cpg).startsWith("class$")
                                && getStatic.getSignature(cpg).equals("Ljava/lang/Class;");
                        for (Iterator<Location> j = cfg.locationIterator(); j.hasNext();) {
                            Location location2 = j.next();
                            if (location2.getHandle().getPosition() + 15 == location.getHandle().getPosition()) {
                                Instruction instruction2 = location2.getHandle().getInstruction();
                                if (instruction2 instanceof LDC) {
                                    Object value = ((LDC) instruction2).getValue(methodGen.getConstantPool());
                                    if (value instanceof String) {
                                        String n = (String) value;
                                        if (n.length() > 0)
                                            initializationOf = ClassName.toSignature(n);
                                    }
                                }
                            }
                        }

                    } else if (prevIns instanceof LDC) {
                        LDC ldc = (LDC) prevIns;
                        Type t = ldc.getType(methodGen.getConstantPool());
                        if (t.getSignature().equals("Ljava/lang/Class;")) {
                            ConstantClass v = (ConstantClass) ldc.getValue(methodGen.getConstantPool());
                            initializationOf = ClassName.toSignature(v.getBytes(javaClass.getConstantPool()));
                            foundDeadClassInitialization = true;
                        } else
                            continue; // not an interesting DLS

                    } else if (prevIns instanceof DUP2) {
                        // Check for the case where, due to the bytecode
                        // compiler, a long is needlessly stored just
                        // after we've DUP2'ed the stack and just
                        // before we return
                        Instruction cur = location.getHandle().getInstruction();
                        Instruction nxt = location.getHandle().getNext().getInstruction();
                        if (cur instanceof LSTORE && nxt instanceof LRETURN) {
                            continue; // not an interesting DLS
                        }
                    }
                    if (foundDeadClassInitialization) {
                        if (classContext.getJavaClass().getSuperclassName().equals("org.apache.axis.client.Stub"))
                            continue;
                        BugInstance bugInstance = new BugInstance(this, "DLS_DEAD_STORE_OF_CLASS_LITERAL",
                                Priorities.NORMAL_PRIORITY).addClassAndMethod(methodGen, sourceFileName).add(lvAnnotation)
                                .addType(initializationOf);
                        accumulator.accumulateBug(bugInstance, sourceLineAnnotation);
                        continue;
                    }

                    if (prevIns instanceof LDC || prevIns instanceof ConstantPushInstruction)
                        propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_CONSTANT);
                    else if (prevIns instanceof ACONST_NULL) {
                        storeOfNull = true;
                        propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_NULL);
                    }
                }

                if (typeOfValue instanceof BasicType || Type.STRING.equals(typeOfValue))
                    propertySet.addProperty(DeadLocalStoreProperty.BASE_VALUE);

                // Ignore assignments that were killed by a subsequent
                // assignment.
                boolean killedBySubsequentStore = llsaDataflow.getAnalysis().killedByStore(liveStoreSet, local);
                if (killedBySubsequentStore) {
                    if (propertySet.containsProperty(DeadLocalStoreProperty.STORE_OF_NULL)
                            || propertySet.containsProperty(DeadLocalStoreProperty.STORE_OF_CONSTANT))
                        continue;
                    propertySet.addProperty(DeadLocalStoreProperty.KILLED_BY_SUBSEQUENT_STORE);
                }

                // Ignore dead assignments of null and 0.
                // These often indicate defensive programming.
                InstructionHandle prev = location.getBasicBlock().getPredecessorOf(location.getHandle());
                int prevOpCode = -1;

                if (prev != null) {
                    if (defensiveConstantValueOpcodes.get(prev.getInstruction().getOpcode())) {
                        propertySet.addProperty(DeadLocalStoreProperty.DEFENSIVE_CONSTANT_OPCODE);
                        prevOpCode = prev.getInstruction().getOpcode();
                    }

                    if (prev.getInstruction() instanceof GETFIELD) {
                        InstructionHandle prev2 = prev.getPrev();

                        if (prev2 != null && prev2.getInstruction() instanceof ALOAD)
                            propertySet.addProperty(DeadLocalStoreProperty.CACHING_VALUE);
                    }
                    if (prev.getInstruction() instanceof LoadInstruction)
                        propertySet.addProperty(DeadLocalStoreProperty.COPY_VALUE);
                    if (prev.getInstruction() instanceof InvokeInstruction)
                        propertySet.addProperty(DeadLocalStoreProperty.METHOD_RESULT);
                }
                boolean deadObjectStore = false;
                if (ins instanceof IINC) {
                    // special handling of IINC

                    if (method.getName().equals("main") && method.isStatic()
                            && method.getSignature().equals("([Ljava/lang/String;)V"))
                        propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_MAIN);

                    propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT);
                    if (localIncrementCount[local] == 1) {
                        propertySet.addProperty(DeadLocalStoreProperty.SINGLE_DEAD_INCREMENT);
                    } else
                        propertySet.removeProperty(DeadLocalStoreProperty.IS_PARAMETER);

                } else if (ins instanceof ASTORE && prev != null) {
                    // Look for objects created but never used

                    Instruction prevIns = prev.getInstruction();
                    if ((prevIns instanceof INVOKESPECIAL && ((INVOKESPECIAL) prevIns).getMethodName(methodGen.getConstantPool())
                            .equals("<init>"))
                            || prevIns instanceof ANEWARRAY
                            || prevIns instanceof NEWARRAY
                            || prevIns instanceof MULTIANEWARRAY) {
                        deadObjectStore = true;
                    } else if (prevIns instanceof DUP)
                        propertySet.addProperty(DeadLocalStoreProperty.DUP_THEN_STORE);
                }
                if (deadObjectStore)
                    propertySet.addProperty(DeadLocalStoreProperty.DEAD_OBJECT_STORE);
                else if (!killedBySubsequentStore && localStoreCount[local] == 2 && localLoadCount[local] > 0) {
                    // TODO: why is this significant?

                    propertySet.addProperty(DeadLocalStoreProperty.TWO_STORES_MULTIPLE_LOADS);

                } else if (!parameterThatIsDeadAtEntry && localStoreCount[local] == 1 && localLoadCount[local] == 0
                        && propertySet.containsProperty(DeadLocalStoreProperty.DEFENSIVE_CONSTANT_OPCODE)) {
                    // might be final local constant
                    propertySet.addProperty(DeadLocalStoreProperty.SINGLE_STORE);

                } else if (!parameterThatIsDeadAtEntry && !propertySet.containsProperty(DeadLocalStoreProperty.SHADOWS_FIELD)
                        && localLoadCount[local] == 0) {
                    // TODO: why is this significant?
                    propertySet.addProperty(DeadLocalStoreProperty.NO_LOADS);
                }
                if (!storeOfNull && typeOfValue != null
                        && !propertySet.containsProperty(DeadLocalStoreProperty.EXCEPTION_HANDLER)) {
                    String signatureOfValue = typeOfValue.getSignature();
                    if ((signatureOfValue.startsWith("Ljava/sql/") || signatureOfValue.startsWith("Ljavax/sql/"))
                            && !signatureOfValue.endsWith("Exception"))
                        propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_DATABASE_VALUE);
                }

                if (parameterThatIsDeadAtEntry) {
                    propertySet.addProperty(DeadLocalStoreProperty.PARAM_DEAD_ON_ENTRY);
                    if (pendingBugReportAboutOverwrittenParameter != null)
                        pendingBugReportAboutOverwrittenParameter.setPriority(Priorities.HIGH_PRIORITY);
                }

                if (localStoreCount[local] > 3)
                    propertySet.addProperty(DeadLocalStoreProperty.MANY_STORES);
                int occurrences = cfg.getLocationsContainingInstructionWithOffset(pc).size();
                if (occurrences > 2 || sourceLineAnnotation.getStartLine() > 0
                        && linesMentionedMultipleTimes.get(sourceLineAnnotation.getStartLine()))
                    propertySet.addProperty(DeadLocalStoreProperty.CLONED_STORE);
                String sourceFile = javaClass.getSourceFileName();
                if (Subtypes2.isJSP(javaClass))
                    propertySet.addProperty(DeadLocalStoreProperty.IN_JSP_PAGE);
                else if (javaClass.isSynthetic() || sourceFile != null && !sourceFile.endsWith(".java")) {
                    if (sourceFile.endsWith(".gxp") && (lvName.startsWith("gxp$") || lvName.startsWith("gxp_")))
                        continue;
                    propertySet.addProperty(DeadLocalStoreProperty.NOT_JAVA);
                }

                // Report the warning
                String bugPattern;
                if (storeOfNull)
                    bugPattern = "DLS_DEAD_LOCAL_STORE_OF_NULL";
                else if (shadowedField != null)
                    bugPattern = "DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD";
                else
                    bugPattern = "DLS_DEAD_LOCAL_STORE";
                BugInstance bugInstance = new BugInstance(this, bugPattern, NORMAL_PRIORITY).addClassAndMethod(methodGen,
                        sourceFileName).add(lvAnnotation);

                if (shadowedField != null)
                    bugInstance.addField(FieldAnnotation.fromBCELField(classContext.getJavaClass(), shadowedField)).describe(
                            FieldAnnotation.DID_YOU_MEAN_ROLE);

                // If in relaxed reporting mode, encode heuristic
                // information.
                if (FindBugsAnalysisFeatures.isRelaxedMode()) {
                    // Add general-purpose warning properties
                    WarningPropertyUtil.addPropertiesForDataMining(propertySet, classContext, method, location);
                }
                // Turn all warning properties into BugProperties
                propertySet.decorateBugInstance(bugInstance);
                if (DEBUG) {
                    System.out.println(sourceFileName + " : " + methodGen.getName());
                    System.out.println("priority: " + bugInstance.getPriority());
                    System.out.println("Reporting " + bugInstance);
                    System.out.println(propertySet);
                }
                accumulator.accumulateBug(bugInstance, sourceLineAnnotation);

            } finally {
                if (pendingBugReportAboutOverwrittenParameter != null)
                    bugReporter.reportBug(pendingBugReportAboutOverwrittenParameter);
            }
        }

        suppressWarningsIfOneLiveStoreOnLine(accumulator, liveStoreSourceLineSet);

        accumulator.reportAccumulatedBugs();
    }

    /**
     * If feature is enabled, suppress warnings where there is at least one live
     * store on the line where the warning would be reported.
     * 
     * @param accumulator
     *            BugAccumulator containing warnings for method
     * @param liveStoreSourceLineSet
     *            bitset of lines where at least one live store was seen
     */
    private void suppressWarningsIfOneLiveStoreOnLine(BugAccumulator accumulator, BitSet liveStoreSourceLineSet) {
        if (!SUPPRESS_IF_AT_LEAST_ONE_LIVE_STORE_ON_LINE) {
            return;
        }

        // Eliminate any accumulated warnings for instructions
        // that (due to inlining) *can* be live stores.
        entryLoop: for (Iterator<? extends BugInstance> i = accumulator.uniqueBugs().iterator(); i.hasNext();) {

            for (SourceLineAnnotation annotation : accumulator.locations(i.next())) {
                if (liveStoreSourceLineSet.get(annotation.getStartLine())) {
                    // This instruction can be a live store; don't report
                    // it as a warning.
                    i.remove();
                    continue entryLoop;
                }
            }
        }
    }

    /**
     * Count stores, loads, and increments of local variables in method whose
     * CFG is given.
     * 
     * @param localStoreCount
     *            counts of local stores (indexed by local)
     * @param localLoadCount
     *            counts of local loads (indexed by local)
     * @param localIncrementCount
     *            counts of local increments (indexed by local)
     * @param cfg
     *            control flow graph (CFG) of method
     */
    private void countLocalStoresLoadsAndIncrements(int[] localStoreCount, int[] localLoadCount, int[] localIncrementCount,
            CFG cfg) {
        for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
            Location location = i.next();

            if (location.getBasicBlock().isExceptionHandler())
                continue;

            boolean isStore = isStore(location);
            boolean isLoad = isLoad(location);
            if (!isStore && !isLoad)
                continue;

            IndexedInstruction ins = (IndexedInstruction) location.getHandle().getInstruction();
            int local = ins.getIndex();
            if (ins instanceof IINC) {
                localStoreCount[local]++;
                localLoadCount[local]++;
                localIncrementCount[local]++;
            } else if (isStore)
                localStoreCount[local]++;
            else
                localLoadCount[local]++;
        }
    }

    /**
     * Get the name of given local variable (if possible) and store it in the
     * HeuristicPropertySet.
     * 
     * @param lvt
     *            the LocalVariableTable
     * @param local
     *            index of the local
     * @param pc
     *            program counter value of the instruction
     */
    private void checkLocalVariableName(LocalVariableTable lvt, int local, int pc,
            WarningPropertySet<DeadLocalStoreProperty> propertySet) {
        if (lvt != null) {
            LocalVariable lv = lvt.getLocalVariable(local, pc);
            if (lv != null) {
                String localName = lv.getName();
                propertySet.setProperty(DeadLocalStoreProperty.LOCAL_NAME, localName);
            }
        }

    }

    /**
     * Is instruction at given location a store?
     * 
     * @param location
     *            the location
     * @return true if instruction at given location is a store, false if not
     */
    private boolean isStore(Location location) {
        Instruction ins = location.getHandle().getInstruction();
        return (ins instanceof StoreInstruction) || (ins instanceof IINC);
    }

    /**
     * Is instruction at given location a load?
     * 
     * @param location
     *            the location
     * @return true if instruction at given location is a load, false if not
     */
    private boolean isLoad(Location location) {
        Instruction ins = location.getHandle().getInstruction();
        return (ins instanceof LoadInstruction) || (ins instanceof IINC);
    }

    public void report() {
    }
}

// vim:ts=4
