diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java index ce4ed9e3f2e6..7f719e60962a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java @@ -8,7 +8,6 @@ import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; -import com.intellij.openapi.util.Key; import com.intellij.patterns.PlatformPatterns; import com.intellij.patterns.PsiElementPattern; import com.intellij.psi.*; @@ -16,9 +15,11 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.impl.source.PostprocessReformattingAspect; +import com.intellij.psi.impl.source.tree.java.PsiEmptyExpressionImpl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author peter @@ -27,7 +28,6 @@ class SmartCastProvider extends CompletionProvider { static final PsiElementPattern.Capture INSIDE_TYPECAST_TYPE = PlatformPatterns.psiElement().afterLeaf( PlatformPatterns.psiElement().withText("(").withParent( PsiTypeCastExpression.class)); - public static final Key TYPE_CAST = Key.create("TYPE_CAST"); protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) { for (final ExpectedTypeInfo type : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { @@ -35,50 +35,90 @@ class SmartCastProvider extends CompletionProvider { continue; } - final boolean overwrite = INSIDE_TYPECAST_TYPE.accepts(parameters.getOriginalPosition()); + final PsiElement originalPosition = parameters.getOriginalPosition(); + final boolean overwrite = INSIDE_TYPECAST_TYPE.accepts(originalPosition); - final LookupElement item = AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE.applyPolicy(new LookupElementDecorator( - PsiTypeLookupItem.createLookupItem(type.getDefaultType(), parameters.getPosition())) { - - @Override - public void handleInsert(InsertionContext context) { - FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.smarttype.casting"); - - final Editor editor = context.getEditor(); - final Document document = editor.getDocument(); - if (overwrite) { - document.deleteString(context.getSelectionEndOffset(), - context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET)); + final PsiType defaultType = type.getDefaultType(); + result.addElement(createSmartCastElement(parameters, overwrite, defaultType)); + if (defaultType instanceof PsiPrimitiveType) { + final PsiType type1 = getCastedExpressionType(originalPosition); + if (type1 != null && !(type1 instanceof PsiPrimitiveType)) { + final PsiClassType boxedType = ((PsiPrimitiveType)defaultType).getBoxedType(originalPosition); + if (boxedType != null) { + result.addElement(createSmartCastElement(parameters, overwrite, boxedType)); } - - final CodeStyleSettings csSettings = CodeStyleSettingsManager.getSettings(context.getProject()); - final int oldTail = context.getTailOffset(); - context.setTailOffset(RParenthTailType.addRParenth(editor, oldTail, csSettings.SPACE_WITHIN_CAST_PARENTHESES)); - - //todo let the delegate handle insertion itself - final LookupItem typeItem = getDelegate(); - final InsertionContext typeContext = CompletionUtil.newContext(context, typeItem, context.getStartOffset(), oldTail); - new DefaultInsertHandler().handleInsert(typeContext, typeItem); - final PsiTypeCastExpression castExpression = - PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiTypeCastExpression.class, false); - if (castExpression != null) { - final PsiTypeElement typeElement = castExpression.getCastType(); - if (typeElement != null) { - CodeStyleManager.getInstance(context.getProject()).reformat(typeElement); - } - } - - PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(); - if (csSettings.SPACE_AFTER_TYPE_CAST) { - context.setTailOffset(TailType.insertChar(editor, context.getTailOffset(), ' ')); - } - - editor.getCaretModel().moveToOffset(context.getTailOffset()); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } - }); - item.putUserData(TYPE_CAST, Boolean.TRUE); - result.addElement(item); + } } } + + @Nullable + private static PsiType getCastedExpressionType(PsiElement originalPosition) { + if (INSIDE_TYPECAST_TYPE.accepts(originalPosition)) { + final PsiTypeCastExpression cast = PsiTreeUtil.getParentOfType(originalPosition, PsiTypeCastExpression.class); + if (cast != null) { + final PsiExpression operand = cast.getOperand(); + return operand == null ? null : operand.getType(); + } + } + final PsiParenthesizedExpression parens = PsiTreeUtil.getParentOfType(originalPosition, PsiParenthesizedExpression.class, true, PsiStatement.class); + if (parens != null) { + final PsiExpression rightSide = parens.getExpression(); + if (rightSide != null) { + return rightSide.getType(); + } + PsiElement next = parens.getNextSibling(); + while (next != null && (next instanceof PsiEmptyExpressionImpl || next instanceof PsiErrorElement || next instanceof PsiWhiteSpace)) { + next = next.getNextSibling(); + } + if (next instanceof PsiExpression) { + return ((PsiExpression)next).getType(); + } + return null; + } + return null; + } + + private static LookupElement createSmartCastElement(final CompletionParameters parameters, final boolean overwrite, final PsiType type) { + return AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE.applyPolicy(new LookupElementDecorator( + PsiTypeLookupItem.createLookupItem(type, parameters.getPosition())) { + + @Override + public void handleInsert(InsertionContext context) { + FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.smarttype.casting"); + + final Editor editor = context.getEditor(); + final Document document = editor.getDocument(); + if (overwrite) { + document.deleteString(context.getSelectionEndOffset(), + context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET)); + } + + final CodeStyleSettings csSettings = CodeStyleSettingsManager.getSettings(context.getProject()); + final int oldTail = context.getTailOffset(); + context.setTailOffset(RParenthTailType.addRParenth(editor, oldTail, csSettings.SPACE_WITHIN_CAST_PARENTHESES)); + + //todo let the delegate handle insertion itself + final LookupItem typeItem = getDelegate(); + final InsertionContext typeContext = CompletionUtil.newContext(context, typeItem, context.getStartOffset(), oldTail); + new DefaultInsertHandler().handleInsert(typeContext, typeItem); + final PsiTypeCastExpression castExpression = + PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiTypeCastExpression.class, false); + if (castExpression != null) { + final PsiTypeElement typeElement = castExpression.getCastType(); + if (typeElement != null) { + CodeStyleManager.getInstance(context.getProject()).reformat(typeElement); + } + } + + PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(); + if (csSettings.SPACE_AFTER_TYPE_CAST) { + context.setTailOffset(TailType.insertChar(editor, context.getTailOffset(), ' ')); + } + + editor.getCaretModel().moveToOffset(context.getTailOffset()); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + }); + } } diff --git a/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing-out.java b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing-out.java new file mode 100644 index 000000000000..bbf0f76d3cf6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing-out.java @@ -0,0 +1,8 @@ +class A { + public abstract void zoo(int y); + + { + Object x; + zoo((Integer) x); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing.java b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing.java new file mode 100644 index 000000000000..661b6ea0de2d --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing.java @@ -0,0 +1,8 @@ +class A { + public abstract void zoo(int y); + + { + Object x; + zoo((x); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing2-out.java b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing2-out.java new file mode 100644 index 000000000000..bbf0f76d3cf6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing2-out.java @@ -0,0 +1,8 @@ +class A { + public abstract void zoo(int y); + + { + Object x; + zoo((Integer) x); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing2.java b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing2.java new file mode 100644 index 000000000000..fede6af603c5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing2.java @@ -0,0 +1,8 @@ +class A { + public abstract void zoo(int y); + + { + Object x; + zoo(() x); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing3-out.java b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing3-out.java new file mode 100644 index 000000000000..bbf0f76d3cf6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing3-out.java @@ -0,0 +1,8 @@ +class A { + public abstract void zoo(int y); + + { + Object x; + zoo((Integer) x); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing3.java b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing3.java new file mode 100644 index 000000000000..4df5d6ae846f --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/CastAutoboxing3.java @@ -0,0 +1,8 @@ +class A { + public abstract void zoo(int y); + + { + Object x; + zoo((a) x); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java index 7150b0a3630c..0f694ed93607 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java @@ -431,6 +431,10 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { public void testPrivateOverloads() throws Throwable { doTest(); } + public void testCastAutoboxing() throws Throwable { doActionItemTest(); } + public void testCastAutoboxing2() throws Throwable { doActionItemTest(); } + public void testCastAutoboxing3() throws Throwable { doActionItemTest(); } + public void testNoFieldsInSuperConstructorCall() throws Throwable { doTest(); } public void testNoUninitializedFieldsInConstructor() throws Throwable { doTest(); } public void testFieldsSetInAnotherConstructor() throws Throwable { doTest(); }