diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java index 477a20bdc953..67755d753503 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java @@ -118,7 +118,7 @@ public class BasicExpressionCompletionContributor extends ExpressionSmartComplet final PsiExpression context = PsiTreeUtil.getParentOfType(position, PsiExpression.class); if (context == null) return; - final Map map = GuessManager.getInstance(position.getProject()).getDataFlowExpressionTypes(context); + final Map map = GuessManager.getInstance(position.getProject()).getControlFlowExpressionTypes(context); for (final PsiExpression expression : map.keySet()) { final PsiType castType = map.get(expression); final PsiType baseType = expression.getType(); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index e8b9bffee027..499e09eab977 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -870,7 +870,7 @@ public class JavaCompletionUtil { type = evaluator.fun(qualifier, parameters); } if (type == null) { - type = GuessManager.getInstance(project).getDataFlowCastedExpressionType(qualifier); + type = GuessManager.getInstance(project).getControlFlowExpressionType(qualifier); } if (type != null) { processor.clear(); diff --git a/java/java-impl/src/com/intellij/codeInsight/guess/GuessManager.java b/java/java-impl/src/com/intellij/codeInsight/guess/GuessManager.java index 410a75112d53..c011ea499666 100644 --- a/java/java-impl/src/com/intellij/codeInsight/guess/GuessManager.java +++ b/java/java-impl/src/com/intellij/codeInsight/guess/GuessManager.java @@ -21,10 +21,8 @@ public abstract class GuessManager { public abstract PsiType[] guessTypeToCast(PsiExpression expr); @NotNull - public abstract Map getDataFlowExpressionTypes(@NotNull PsiExpression forPlace); + public abstract Map getControlFlowExpressionTypes(@NotNull PsiExpression forPlace); @Nullable - public PsiType getDataFlowCastedExpressionType(@NotNull PsiExpression expr) { - return getDataFlowExpressionTypes(expr).get(expr); - } + public abstract PsiType getControlFlowExpressionType(@NotNull PsiExpression expr); } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java index 3e1f047cbc1c..4e154cca2bbc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java @@ -17,6 +17,7 @@ import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; @@ -102,7 +103,7 @@ public class GuessManagerImpl extends GuessManager { public PsiType[] guessTypeToCast(PsiExpression expr) { //TODO : make better guess based on control flow LinkedHashSet types = new LinkedHashSet(); - ContainerUtil.addIfNotNull(_getDataFlowExpressionTypes(expr).get(expr), types); + ContainerUtil.addIfNotNull(getControlFlowExpressionType(expr), types); addExprTypesWhenContainerElement(types, expr); addExprTypesByDerivedClasses(types, expr); @@ -111,12 +112,17 @@ public class GuessManagerImpl extends GuessManager { @NotNull @Override - public Map getDataFlowExpressionTypes(@NotNull final PsiExpression forPlace) { - return _getDataFlowExpressionTypes(forPlace); + public Map getControlFlowExpressionTypes(@NotNull final PsiExpression forPlace) { + final Map typeMap = buildDataflowTypeMap(forPlace); + if (typeMap != null) { + return typeMap; + } + + return getAllTypeCasts(forPlace); } - @NotNull - private static Map _getDataFlowExpressionTypes(final PsiExpression forPlace) { + @Nullable + private static Map buildDataflowTypeMap(PsiExpression forPlace) { PsiElement scope = DfaUtil.getTopmostBlockInSameClass(forPlace); if (scope == null) { return Collections.emptyMap(); @@ -130,12 +136,57 @@ public class GuessManagerImpl extends GuessManager { final ExpressionTypeInstructionVisitor visitor = new ExpressionTypeInstructionVisitor(forPlace); if (runner.analyzeMethod(scope, visitor) == RunnerResult.OK) { - final Map map = visitor.getResult(); - if (map != null) { - return map; - } + return visitor.getResult(); } - return Collections.emptyMap(); + return null; + } + + private static Map getAllTypeCasts(PsiExpression forPlace) { + final int start = forPlace.getTextRange().getStartOffset(); + final Map allCasts = new THashMap(ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY); + getTopmostBlock(forPlace).accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitTypeCastExpression(PsiTypeCastExpression expression) { + final PsiTypeElement castType = expression.getCastType(); + final PsiExpression operand = expression.getOperand(); + if (operand != null && castType != null) { + allCasts.put(operand, castType.getType()); + } + super.visitTypeCastExpression(expression); + } + + @Override + public void visitInstanceOfExpression(PsiInstanceOfExpression expression) { + final PsiTypeElement castType = expression.getCheckType(); + final PsiExpression operand = expression.getOperand(); + if (castType != null) { + allCasts.put(operand, castType.getType()); + } + super.visitInstanceOfExpression(expression); + } + + @Override + public void visitElement(PsiElement element) { + if (element.getTextRange().getStartOffset() > start) { + return; + } + + super.visitElement(element); + } + }); + return allCasts; + } + + private static PsiElement getTopmostBlock(PsiElement scope) { + PsiElement lastScope = scope; + while (true) { + final PsiCodeBlock lastCodeBlock = PsiTreeUtil.getParentOfType(lastScope, PsiCodeBlock.class, true); + if (lastCodeBlock == null) { + break; + } + lastScope = lastCodeBlock; + } + return lastScope; } private void addExprTypesByDerivedClasses(LinkedHashSet set, PsiExpression expr) { @@ -293,6 +344,21 @@ public class GuessManagerImpl extends GuessManager { return null; } + @Nullable + public PsiType getControlFlowExpressionType(@NotNull PsiExpression expr) { + final Map allCasts = getAllTypeCasts(expr); + if (!allCasts.containsKey(expr)) { + return null; //optimization + } + + final Map fromDfa = buildDataflowTypeMap(expr); + if (fromDfa != null) { + return fromDfa.get(expr); + } + + return allCasts.get(expr); + } + private static class ExpressionTypeInstructionVisitor extends InstructionVisitor { private Map myResult; private PsiElement myForPlace;