/* * Copyright 2011 Jon S Akhtar (Sylvanaar) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sylvanaar.idea.Lua.editor.inspections.usage; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.DebugUtil; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Processor; import com.sylvanaar.idea.Lua.editor.inspections.AbstractInspection; import com.sylvanaar.idea.Lua.lang.psi.LuaControlFlowOwner; import com.sylvanaar.idea.Lua.lang.psi.LuaPsiElement; import com.sylvanaar.idea.Lua.lang.psi.LuaPsiFile; import com.sylvanaar.idea.Lua.lang.psi.LuaReferenceElement; import com.sylvanaar.idea.Lua.lang.psi.controlFlow.Instruction; import com.sylvanaar.idea.Lua.lang.psi.controlFlow.ReadWriteVariableInstruction; import com.sylvanaar.idea.Lua.lang.psi.dataFlow.DFAEngine; import com.sylvanaar.idea.Lua.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsDfaInstance; import com.sylvanaar.idea.Lua.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsSemilattice; import com.sylvanaar.idea.Lua.lang.psi.statements.LuaAssignmentStatement; import com.sylvanaar.idea.Lua.lang.psi.statements.LuaBlock; import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaLocal; import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaParameter; import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaSymbol; import com.sylvanaar.idea.Lua.lang.psi.visitor.LuaElementVisitor; import gnu.trove.TIntHashSet; import gnu.trove.TIntObjectHashMap; import gnu.trove.TIntProcedure; import gnu.trove.TObjectProcedure; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; /** & @author ven */ public class UnusedDefInspection extends AbstractInspection { private static final Logger log = Logger.getInstance("Lua.UnusedDefInspection"); @Override public String getStaticDescription() { return "Variable is not used"; } @Nls @NotNull public String getGroupDisplayName() { return "Data Flow Issues"; } @Nls @NotNull public String getDisplayName() { return "Unused Assignment"; } @NonNls @NotNull public String getShortName() { return "LuaUnusedAssignment"; } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new LuaElementVisitor() { // @Override // public void visitBlock(LuaBlock e) { // super.visitBlock(e); // // check(e, holder); // } @Override public void visitFile(PsiFile file) { super.visitFile(file); check((LuaControlFlowOwner) file, holder); } }; } protected void check(final LuaControlFlowOwner owner, final ProblemsHolder problemsHolder) { final Instruction[] flow = owner.getControlFlow(); final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow); final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice(); final DFAEngine<TIntObjectHashMap<TIntHashSet>> engine = new DFAEngine<TIntObjectHashMap<TIntHashSet>>(flow, dfaInstance, lattice); final ArrayList<TIntObjectHashMap<TIntHashSet>> dfaResult = engine.performDFA(); final TIntHashSet unusedDefs = new TIntHashSet(); for (Instruction instruction : flow) { if (instruction instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction) instruction).isWrite()) { unusedDefs.add(instruction.num()); } } for (int i = 0; i < dfaResult.size(); i++) { final Instruction instruction = flow[i]; if (instruction instanceof ReadWriteVariableInstruction) { final ReadWriteVariableInstruction varInsn = (ReadWriteVariableInstruction) instruction; if (!varInsn.isWrite()) { final String varName = varInsn.getVariableName(); TIntObjectHashMap<TIntHashSet> e = dfaResult.get(i); e.forEachValue(new TObjectProcedure<TIntHashSet>() { public boolean execute(TIntHashSet reaching) { reaching.forEach(new TIntProcedure() { public boolean execute(int defNum) { final String defName = ((ReadWriteVariableInstruction) flow[defNum]).getVariableName(); if (varName.equals(defName)) { unusedDefs.remove(defNum); } return true; } }); return true; } }); } } } unusedDefs.forEach(new TIntProcedure() { public boolean execute(int num) { final ReadWriteVariableInstruction instruction = (ReadWriteVariableInstruction)flow[num]; final PsiElement element = instruction.getElement(); if (element == null) return true; PsiElement toHighlight = null; if (isLocalAssignment(element)) { if (element instanceof LuaReferenceElement) { PsiElement parent = element.getParent(); if (parent instanceof LuaReferenceElement) { toHighlight = ((LuaAssignmentStatement)parent).getLeftExprs(); } // if (parent instanceof GrPostfixExpression) { // toHighlight = parent; // } } else if (element instanceof LuaSymbol) { toHighlight = ((LuaSymbol)element);//.getNamedElement(); } if (toHighlight == null) toHighlight = element; problemsHolder.registerProblem(toHighlight, "Unused Assignment", ProblemHighlightType.LIKE_UNUSED_SYMBOL); } return true; } }); } private boolean isUsedInToplevelFlowOnly(PsiElement element) { LuaSymbol var = null; if (element instanceof LuaSymbol) { var = (LuaSymbol) element; } else if (element instanceof LuaReferenceElement) { final PsiElement resolved = ((LuaReferenceElement) element).resolve(); if (resolved instanceof LuaSymbol) var = (LuaSymbol) resolved; } if (var != null) { final LuaPsiElement scope = getScope(var); if (scope == null) { PsiFile file = var.getContainingFile(); log.error(file == null ? "no file???" : DebugUtil.psiToString(file, true, false)); } return ReferencesSearch.search(var, new LocalSearchScope(scope)).forEach(new Processor<PsiReference>() { public boolean process(PsiReference ref) { return getScope(ref.getElement()) == scope; } }); } return true; } private LuaPsiElement getScope(PsiElement var) { return PsiTreeUtil.getParentOfType(var, LuaBlock.class, LuaPsiFile.class); } private boolean isLocalAssignment(PsiElement element) { if (element instanceof LuaSymbol) { return isLocalVariable((LuaSymbol) element, false); } else if (element instanceof LuaReferenceElement) { final PsiElement resolved = ((LuaReferenceElement) element).resolve(); return resolved instanceof LuaSymbol && isLocalVariable((LuaSymbol) resolved, true); } return false; } private boolean isLocalVariable(LuaSymbol var, boolean parametersAllowed) { if (var instanceof LuaLocal) return true; else if (var instanceof LuaParameter && !parametersAllowed) return false; return false; } public boolean isEnabledByDefault() { return false; } }