IDEA-82839 Skip too complex methods in Groovy DFA analysis

This commit is contained in:
Maxim.Medvedev
2012-03-21 14:21:34 +04:00
parent 5ffd29ad22
commit addebb5e34
7 changed files with 82 additions and 26 deletions
@@ -302,3 +302,5 @@ method.with.type.parameters.should.have.return.type=Method with type parameters
primitive.type.parameters.are.not.allowed=Primitive type parameters are not allowed
primitive.bound.types.are.not.allowed=Primitive bound types are not allowed
ellipsis.type.is.not.allowed.here=Ellipsis type is not allowed here
method.0.is.too.complex.too.analyze=Method ''{0}'' is too complex to analyze.\nTypes of local variables are not inferred.
closure.is.too.complex.to.analyze=Closure is complex to analyze.\nTypes of local variables are not inferred.
@@ -99,6 +99,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatem
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.types.*;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner;
import org.jetbrains.plugins.groovy.lang.psi.impl.TypeInferenceHelper;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
@@ -532,6 +533,12 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
checkMethodWithTypeParamsShouldHaveReturnType(myHolder, method);
checkInnerMethod(myHolder, method);
checkMethodParameters(myHolder, method);
GrOpenBlock block = method.getBlock();
if (block != null && TypeInferenceHelper.isTooComplexTooAnalyze(block)) {
myHolder.createWeakWarningAnnotation(method.getNameIdentifierGroovy(), GroovyBundle.message("method.0.is.too.complex.too.analyze",
method.getName()));
}
}
private static void checkMethodWithTypeParamsShouldHaveReturnType(AnnotationHolder holder, GrMethod method) {
@@ -936,6 +943,20 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
if (!closure.hasParametersSection() && isClosureAmbiguous(closure)) {
myHolder.createErrorAnnotation(closure, GroovyBundle.message("ambiguous.code.block"));
}
if (TypeInferenceHelper.isTooComplexTooAnalyze(closure)) {
int startOffset = closure.getLBrace().getTextRange().getStartOffset();
int endOffset;
if (closure.getArrow()!=null) {
endOffset = closure.getArrow().getTextRange().getEndOffset();
}
else {
String text =
PsiDocumentManager.getInstance(closure.getProject()).getDocument(closure.getContainingFile()).getText();
endOffset = Math.min(closure.getTextRange().getEndOffset(), text.indexOf('\n', startOffset));
}
myHolder.createWeakWarningAnnotation(new TextRange(startOffset, endOffset), GroovyBundle.message("closure.is.too.complex.to.analyze"));
}
}
private static boolean isClosureAmbiguous(GrClosableBlock closure) {
@@ -83,7 +83,11 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase {
final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow);
final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice();
final DFAEngine<TIntObjectHashMap<TIntHashSet>> engine = new DFAEngine<TIntObjectHashMap<TIntHashSet>>(flow, dfaInstance, lattice);
final List<TIntObjectHashMap<TIntHashSet>> dfaResult = engine.performDFA();
final List<TIntObjectHashMap<TIntHashSet>> dfaResult = engine.performDFAWithTimeout();
if (dfaResult == null) {
return;
}
final TIntHashSet unusedDefs = new TIntHashSet();
for (Instruction instruction : flow) {
if (instruction instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction) instruction).isWrite()) {
@@ -153,17 +153,13 @@ public class ControlFlowUtils {
if (openBlockMayCompleteNormally(tryBlock)) {
return true;
}
final GrCatchClause[] catchClauses = tryStatement.getCatchClauses();
if (catchClauses != null) {
for (GrCatchClause catchClause : catchClauses) {
if (openBlockMayCompleteNormally(catchClause.getBody())) {
return true;
}
for (GrCatchClause catchClause : tryStatement.getCatchClauses()) {
if (openBlockMayCompleteNormally(catchClause.getBody())) {
return true;
}
}
return false;
}
@@ -650,6 +646,7 @@ public class ControlFlowUtils {
place = place.getContext();
}
while (true) {
assert place != null;
place = place.getContext();
if (place == null) return null;
if (place instanceof GrClosableBlock) return (GrClosableBlock)place;
@@ -740,6 +737,7 @@ public class ControlFlowUtils {
});
}
@NotNull
public static ArrayList<BitSet> inferWriteAccessMap(final Instruction[] flow, final GrVariable var) {
final Semilattice<BitSet> sem = new Semilattice<BitSet>() {
@@ -790,7 +788,7 @@ public class ControlFlowUtils {
}
};
return new DFAEngine<BitSet>(flow, dfa, sem).performDFA();
return new DFAEngine<BitSet>(flow, dfa, sem).performForceDFA();
}
}
@@ -16,6 +16,8 @@
package org.jetbrains.plugins.groovy.lang.psi.dataFlow;
import com.intellij.openapi.progress.ProgressManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.CallEnvironment;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.CallInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ControlFlowBuilderUtil;
@@ -27,6 +29,8 @@ import java.util.*;
* @author ven
*/
public class DFAEngine<E> {
private static final long ourTimeLimit = 1000;
private final Instruction[] myFlow;
private final DfaInstance<E> myDfa;
@@ -57,7 +61,22 @@ public class DFAEngine<E> {
}
}
public ArrayList<E> performDFA() {
@NotNull
public ArrayList<E> performForceDFA() {
ArrayList<E> result = performDFA(false);
assert result != null;
return result;
}
@Nullable
public ArrayList<E> performDFAWithTimeout() {
return performDFA(true);
}
@Nullable
private ArrayList<E> performDFA(boolean timeout) {
long startTime = System.currentTimeMillis();
ArrayList<E> info = new ArrayList<E>(myFlow.length);
CallEnvironment env = new MyCallEnvironment(myFlow.length);
for (int i = 0; i < myFlow.length; i++) {
@@ -78,6 +97,8 @@ public class DFAEngine<E> {
visited[instr.num()] = true;
while (!workList.isEmpty()) {
if (timeout && System.currentTimeMillis() - startTime > ourTimeLimit) return null;
ProgressManager.checkCanceled();
final Instruction curr = workList.remove();
final int num = curr.num();
@@ -53,6 +53,7 @@ public class ReachingDefinitionsCollector {
private ReachingDefinitionsCollector() {
}
@NotNull
public static FragmentVariableInfos obtainVariableFlowInformation(final GrStatement first, final GrStatement last) {
GroovyPsiElement context = PsiTreeUtil.getParentOfType(first, GrMethod.class, GrClosableBlock.class, GroovyFileBase.class, GrClassInitializer.class);
GrControlFlowOwner flowOwner;
@@ -66,7 +67,7 @@ public class ReachingDefinitionsCollector {
final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow);
final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice();
final DFAEngine<TIntObjectHashMap<TIntHashSet>> engine = new DFAEngine<TIntObjectHashMap<TIntHashSet>>(flow, dfaInstance, lattice);
final TIntObjectHashMap<TIntHashSet> dfaResult = postprocess(engine.performDFA(), flow, dfaInstance);
final TIntObjectHashMap<TIntHashSet> dfaResult = postprocess(engine.performForceDFA(), flow, dfaInstance);
final LinkedHashSet<Integer> fragmentInstructions = getFragmentInstructions(first, last, flow);
final int[] postorder = ControlFlowBuilderUtil.postorder(flow);
@@ -149,16 +150,15 @@ public class ReachingDefinitionsCollector {
}
String name = variable.getName();
if (name != null) {
if (!(variable instanceof GrField)) {
if (!isInFragment(first, last, resolved)) {
if (isInFragment(first, last, closure)) {
addVariable(name, imap, variable.getManager(), variable.getType());
}
} else {
if (!isInFragment(first, last, closure)) {
addVariable(name, omap, variable.getManager(), variable.getType());
}
if (!(variable instanceof GrField)) {
if (!isInFragment(first, last, resolved)) {
if (isInFragment(first, last, closure)) {
addVariable(name, imap, variable.getManager(), variable.getType());
}
}
else {
if (!isInFragment(first, last, closure)) {
addVariable(name, omap, variable.getManager(), variable.getType());
}
}
}
@@ -306,7 +306,7 @@ public class ReachingDefinitionsCollector {
final StringBuffer buffer = new StringBuffer();
for (int i = 0; i < dfaResult.size(); i++) {
TIntObjectHashMap<TIntHashSet> map = dfaResult.get(i);
buffer.append("At " + i + ":\n");
buffer.append("At ").append(i).append(":\n");
map.forEachEntry(new TIntObjectProcedure<TIntHashSet>() {
public boolean execute(int i, TIntHashSet defs) {
buffer.append(i).append(" -> ");
@@ -366,7 +366,10 @@ public class ReachingDefinitionsCollector {
}
}
private static TIntObjectHashMap<TIntHashSet> postprocess(final ArrayList<TIntObjectHashMap<TIntHashSet>> dfaResult, Instruction[] flow, ReachingDefinitionsDfaInstance dfaInstance) {
@NotNull
private static TIntObjectHashMap<TIntHashSet> postprocess(@NotNull final ArrayList<TIntObjectHashMap<TIntHashSet>> dfaResult,
@NotNull Instruction[] flow,
@NotNull ReachingDefinitionsDfaInstance dfaInstance) {
TIntObjectHashMap<TIntHashSet> result = new TIntObjectHashMap<TIntHashSet>();
for (int i = 0; i < flow.length; i++) {
Instruction insn = flow[i];
@@ -89,6 +89,10 @@ public class TypeInferenceHelper {
return getInferredType(variableName, instruction, flow, scope);
}
public static boolean isTooComplexTooAnalyze(GrControlFlowOwner scope) {
return getDefUseMaps(scope).second == null;
}
@Nullable
private static Instruction findInstructionAt(PsiElement place, Instruction[] flow) {
List<Instruction> applicable = new ArrayList<Instruction>();
@@ -131,7 +135,10 @@ public class TypeInferenceHelper {
final Pair<ReachingDefinitionsDfaInstance, List<TIntObjectHashMap<TIntHashSet>>> pair = getDefUseMaps(scope);
final int varIndex = pair.first.getVarIndex(varName);
final TIntObjectHashMap<TIntHashSet> allDefs = pair.second.get(instruction.num());
List<TIntObjectHashMap<TIntHashSet>> second = pair.second;
if (second == null) return null;
final TIntObjectHashMap<TIntHashSet> allDefs = second.get(instruction.num());
final TIntHashSet varDefs = allDefs.get(varIndex);
if (varDefs == null) return null;
@@ -174,7 +181,7 @@ public class TypeInferenceHelper {
};
final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice();
final DFAEngine<TIntObjectHashMap<TIntHashSet>> engine = new DFAEngine<TIntObjectHashMap<TIntHashSet>>(flow, dfaInstance, lattice);
final List<TIntObjectHashMap<TIntHashSet>> dfaResult = engine.performDFA();
final List<TIntObjectHashMap<TIntHashSet>> dfaResult = engine.performDFAWithTimeout();
return Result.create(Pair.create(dfaInstance, dfaResult), PsiModificationTracker.MODIFICATION_COUNT);
}
});