mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-82839 Skip too complex methods in Groovy DFA analysis
This commit is contained in:
@@ -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) {
|
||||
|
||||
+5
-1
@@ -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()) {
|
||||
|
||||
+7
-9
@@ -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();
|
||||
|
||||
+16
-13
@@ -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];
|
||||
|
||||
+9
-2
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user