From ae31fc2f2706622343a7202fdf6f7f02458ef102 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Mon, 10 Sep 2012 12:03:22 +0400 Subject: [PATCH 1/4] Correctly derive the language from root file type (a case of XML/HTML elements mix) --- .../formatting/templateLanguages/DataLanguageBlockWrapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/templateLanguages/DataLanguageBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/templateLanguages/DataLanguageBlockWrapper.java index a810d1c9b238..26efa42a20cf 100644 --- a/platform/lang-impl/src/com/intellij/formatting/templateLanguages/DataLanguageBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/templateLanguages/DataLanguageBlockWrapper.java @@ -53,7 +53,7 @@ public class DataLanguageBlockWrapper implements ASTBlock, BlockEx, BlockWithPar if (node != null) { final PsiElement psi = node.getPsi(); if (psi != null) { - language = psi.getLanguage(); + language = psi.getContainingFile().getLanguage(); } } myLanguage = language; From 5a546b640cc44df23d9202257110e9830a79d54e Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 10 Sep 2012 12:24:31 +0400 Subject: [PATCH 2/4] lambda: missed return statement error; do not use containing method return type for lambda inference --- .../analysis/HighlightControlFlowUtil.java | 18 +++++--- .../impl/analysis/HighlightVisitorImpl.java | 28 ++++++++++-- .../source/resolve/PsiResolveHelperImpl.java | 14 ++++-- .../lambda/highlighting/ConditionalExpr.java | 2 +- .../highlighting/IncompatibleReturnTypes.java | 8 ++-- .../lambda/highlighting/ReturnValue.java | 43 +++++++++++++++++++ .../highlighting/UnreachableStatement.java | 9 ++++ .../daemon/lambda/LambdaHighlightingTest.java | 8 ++++ 8 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnValue.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/UnreachableStatement.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java index bfd2f6e5cf79..1e5416cdc798 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java @@ -48,11 +48,11 @@ public class HighlightControlFlowUtil { private HighlightControlFlowUtil() { } @Nullable - public static HighlightInfo checkMissingReturnStatement(PsiMethod method) { - PsiCodeBlock body = method.getBody(); + public static HighlightInfo checkMissingReturnStatement(PsiCodeBlock body, PsiType returnType) { + if (body == null - || method.getReturnType() == null - || PsiType.VOID.equals(method.getReturnType())) { + || returnType == null + || PsiType.VOID.equals(returnType)) { return null; } // do not compute constant expressions for if() statement condition @@ -68,9 +68,13 @@ public class HighlightControlFlowUtil { HighlightInfoType.ERROR, context, JavaErrorMessages.message("missing.return.statement")); - QuickFixAction.registerQuickFixAction(highlightInfo, new AddReturnFix(method)); - IntentionAction fix = QUICK_FIX_FACTORY.createMethodReturnFix(method, PsiType.VOID, true); - QuickFixAction.registerQuickFixAction(highlightInfo, fix); + final PsiElement parent = body.getParent(); + if (parent instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)parent; + QuickFixAction.registerQuickFixAction(highlightInfo, new AddReturnFix(method)); + IntentionAction fix = QUICK_FIX_FACTORY.createMethodReturnFix(method, PsiType.VOID, true); + QuickFixAction.registerQuickFixAction(highlightInfo, fix); + } return highlightInfo; } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index a11968f67cbd..3dc1cb631d88 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -282,6 +282,12 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } else { myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, "Lambda expression not expected here")); } + if (!myHolder.hasErrorResults()) { + final PsiElement body = expression.getBody(); + if (body instanceof PsiCodeBlock) { + myHolder.add(HighlightControlFlowUtil.checkUnreachableStatement((PsiCodeBlock)body)); + } + } } } @@ -333,10 +339,24 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh super.visitJavaToken(token); if (!myHolder.hasErrorResults() && token.getTokenType() == JavaTokenType.RBRACE - && token.getParent() instanceof PsiCodeBlock - && token.getParent().getParent() instanceof PsiMethod) { - PsiMethod method = (PsiMethod)token.getParent().getParent(); - myHolder.add(HighlightControlFlowUtil.checkMissingReturnStatement(method)); + && token.getParent() instanceof PsiCodeBlock) { + + final PsiElement gParent = token.getParent().getParent(); + final PsiCodeBlock codeBlock; + final PsiType returnType; + if (gParent instanceof PsiMethod) { + PsiMethod method = (PsiMethod)gParent; + codeBlock = method.getBody(); + returnType = method.getReturnType(); + } else if (gParent instanceof PsiLambdaExpression) { + final PsiElement body = ((PsiLambdaExpression)gParent).getBody(); + if (!(body instanceof PsiCodeBlock)) return; + codeBlock = (PsiCodeBlock)body; + returnType = LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)gParent); + } else { + return; + } + myHolder.add(HighlightControlFlowUtil.checkMissingReturnStatement(codeBlock, returnType)); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java index ac9a3ed686eb..21b208db2aac 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java @@ -850,9 +850,17 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { } } else if (parent instanceof PsiReturnStatement) { - PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); - if (method != null) { - expectedType = method.getReturnType(); + final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class); + if (lambdaExpression != null) { + expectedType = LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression.getFunctionalInterfaceType()); + if (expectedType == null) { + return getFailedInferenceConstraint(typeParameter); + } + } else { + PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); + if (method != null) { + expectedType = method.getReturnType(); + } } } else if (parent instanceof PsiExpressionList) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ConditionalExpr.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ConditionalExpr.java index a083d9ec1b6f..e44f0de64815 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ConditionalExpr.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ConditionalExpr.java @@ -25,7 +25,7 @@ class Test { { boolean flag = true; I i = flag ? (() -> 123) : (() -> 222); - I i1 = flag ? (() -> {}) : (() -> 222); + I i1 = flag ? (() -> {}) : (() -> 222); Object i2 = flag ? (() -> 42) : (() -> 222); I i3 = flag ? ((x) -> 42) : (() -> 222); I i4 = flag ? (() -> 42) : new I() { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IncompatibleReturnTypes.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IncompatibleReturnTypes.java index 61b13e5c0e04..7c16a8b75723 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IncompatibleReturnTypes.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IncompatibleReturnTypes.java @@ -18,7 +18,7 @@ class Test2 { } { IntReturnType aI = () -> System.out.println(); - IntReturnType aI1 = () -> {System.out.println();}; + IntReturnType aI1 = () -> {System.out.println();}; IntReturnType aI2 = () -> {return 1;}; IntReturnType aI3 = () -> 1; } @@ -32,10 +32,10 @@ class Test3 { } { XReturnType aI = () -> System.out.println(); - XReturnType aI1 = () -> {System.out.println();}; + XReturnType aI1 = () -> {System.out.println();}; XReturnType aI2 = () -> {return 1;}; XReturnType aI3 = () -> 1; - XReturnType aI4 = () -> {}; + XReturnType aI4 = () -> {}; } } @@ -48,7 +48,7 @@ class Test4 { { YXReturnType aI = () -> System.out.println(); - YXReturnType aI1 = () -> {System.out.println();}; + YXReturnType aI1 = () -> {System.out.println();}; YXReturnType aI2 = () -> {return 1;}; YXReturnType aI3 = () -> 1; YXReturnType aI4 = () -> new Y(){}; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnValue.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnValue.java new file mode 100644 index 000000000000..5bfdab7f6cbe --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnValue.java @@ -0,0 +1,43 @@ +class Test1 { + interface Extractor { + Option unapply(T t); + } + + public static abstract class Option { + private static class None extends Option {} + + private static final Option NONE = new None(); + + public static Option none() { + return NONE; + } + + public static Option option(T value) { + if (value == null) { + return NONE; + } else { + return null; + } + } + } + + + + public static void main(String[] args) { + Extractor e = s -> { + if (s.equals("1")) { + return Option.option(1); + } else { + return Option.none(); + } + }; + + Extractor e1 = s -> { + if (s.equals("1")) { + return Option.option(1); + } else { + return Option.option("2"); + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/UnreachableStatement.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/UnreachableStatement.java new file mode 100644 index 000000000000..8ce86440acad --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/UnreachableStatement.java @@ -0,0 +1,9 @@ +class Test1 { + { + Comparable c = o -> { + if (o == null) return 1; + return -1; + System.out.println("Hello"); + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java index 5b654b928770..2aba17f529eb 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java @@ -132,6 +132,14 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testVariableInitialization() throws Exception { doTest(); } + + public void testUnreachableStatement() throws Exception { + doTest(); + } + + public void testReturnValue() throws Exception { + doTest(); + } private void doTest() throws Exception { doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); From 11a2faaf142c9838af218fac0a1709a120cce2ef Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 10 Sep 2012 12:43:36 +0400 Subject: [PATCH 3/4] lambda: allow raw types when no inference was performed --- .../src/com/intellij/psi/LambdaUtil.java | 15 ++++++++++++++- .../lambda/highlighting/LambdaRawOrNot.java | 12 ++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 20f87a9b2841..64d5b1246585 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -92,12 +92,25 @@ public class LambdaUtil { public static boolean isLambdaFullyInferred(PsiLambdaExpression expression, PsiType functionalInterfaceType) { if (expression.getParameterList().getParametersCount() > 0 || getFunctionalInterfaceReturnType(functionalInterfaceType) != PsiType.VOID) { //todo check that void lambdas without params check - if (functionalInterfaceType instanceof PsiClassType && ((PsiClassType)functionalInterfaceType).isRaw()) return false; + if (!checkRawAcceptable(expression, functionalInterfaceType)) { + return false; + } return !dependsOnTypeParams(functionalInterfaceType, functionalInterfaceType, expression, null); } return true; } + private static boolean checkRawAcceptable(PsiLambdaExpression expression, PsiType functionalInterfaceType) { + PsiElement parent = expression.getParent(); + while (parent instanceof PsiParenthesizedExpression) { + parent = parent.getParent(); + } + if (parent instanceof PsiExpressionList && functionalInterfaceType instanceof PsiClassType && ((PsiClassType)functionalInterfaceType).isRaw()){ + return false; + } + return true; + } + @Nullable public static String checkInterfaceFunctional(PsiType functionalInterfaceType) { final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(functionalInterfaceType); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/LambdaRawOrNot.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/LambdaRawOrNot.java index cfece56e219b..4b9b085ce780 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/LambdaRawOrNot.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/LambdaRawOrNot.java @@ -58,3 +58,15 @@ class Test4 { public interface TerminalOp1 extends IntermediateOp1 {} } + +class Test5 { + { + Block empty = x -> {}; + Block empty1 = x -> {}; + System.out.println((Block) x -> {}); + } + + interface Block { + void apply(T t); + } +} \ No newline at end of file From 2c659ba2d9b739764ecd1e3696868cfb304cfcd8 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 10 Sep 2012 12:53:36 +0400 Subject: [PATCH 4/4] redundant lambda code block: do not suggest for expressions with non-void type --- .../RedundantLambdaCodeBlockInspection.java | 7 ++++++- .../beforeReturnTypeInExpressionStatement.java | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeReturnTypeInExpressionStatement.java diff --git a/java/java-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java b/java/java-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java index e190687f430e..04d6533f8ea4 100644 --- a/java/java-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java @@ -97,7 +97,12 @@ public class RedundantLambdaCodeBlockInspection extends BaseJavaLocalInspectionT return returnStatement.getReturnValue(); } else { - return ((PsiExpressionStatement)statements[0]).getExpression(); + final PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression(); + final PsiType psiType = expression.getType(); + if (psiType != PsiType.VOID) { + return null; + } + return expression; } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeReturnTypeInExpressionStatement.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeReturnTypeInExpressionStatement.java new file mode 100644 index 000000000000..cd86ba278aff --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeReturnTypeInExpressionStatement.java @@ -0,0 +1,8 @@ +// "Replace with one line expression" "false" +class Test { + { + Runnable c = () -> {foo();}; + } + + int foo() {return 1;} +} \ No newline at end of file