From 220dd971813c20854c590ac9b5f325a51fefed49 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 18 May 2011 16:48:25 +0400 Subject: [PATCH] [ann] IDEA-69800 (unrestricted cast of argument types of JSR-292 "polymorphic" methods) --- .../daemon/impl/analysis/HighlightUtil.java | 22 +----- .../RedundantCastInspection.java | 15 ++-- .../advHighlighting7/PolymorphicTypeCast.java | 6 ++ .../daemon/LightAdvHighlightingJdk7Test.java | 7 +- .../intellij/psi/util/RedundantCastUtil.java | 77 +++++++++++++------ 5 files changed, 71 insertions(+), 56 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index f5d552fc9ba6..605d40c91ec4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -17,7 +17,6 @@ package com.intellij.codeInsight.daemon.impl.analysis; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.codeInsight.daemon.impl.HighlightInfo; @@ -343,10 +342,12 @@ public class HighlightUtil { PsiType castType = castTypeElement.getType(); PsiExpression operand = expression.getOperand(); - if (operand == null || isPolymorphicCall(operand)) return null; + if (operand == null) return null; PsiType operandType = operand.getType(); - if (operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType)) { + if (operandType != null && + !TypeConversionUtil.areTypesConvertible(operandType, castType) && + !RedundantCastUtil.isInPolymorphicCall(expression)) { String message = JavaErrorMessages.message("inconvertible.type.cast", formatType(operandType), formatType(castType)); return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, message); } @@ -354,21 +355,6 @@ public class HighlightUtil { return null; } - /** - * Signature polymorphism - */ - private static boolean isPolymorphicCall(final PsiExpression expression) { - if (PsiUtil.isLanguageLevel7OrHigher(expression) && - expression instanceof PsiMethodCallExpression) { - final PsiElement method = ((PsiMethodCallExpression)expression).getMethodExpression().resolve(); - if (method instanceof PsiMethod && - AnnotationUtil.isAnnotated((PsiMethod)method, CommonClassNames.JAVA_LANG_INVOKE_MH_POLYMORPHIC, false, true)) { - return true; - } - } - return false; - } - @Nullable static HighlightInfo checkVariableExpected(PsiExpression expression) { PsiExpression lValue; diff --git a/java/java-impl/src/com/intellij/codeInspection/redundantCast/RedundantCastInspection.java b/java/java-impl/src/com/intellij/codeInspection/redundantCast/RedundantCastInspection.java index 0b2611b9c4e2..e6a3878700b9 100644 --- a/java/java-impl/src/com/intellij/codeInspection/redundantCast/RedundantCastInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/redundantCast/RedundantCastInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * Created by IntelliJ IDEA. - * User: max - * Date: Dec 24, 2001 - * Time: 2:46:32 PM - * To change template for new class use - * Code Style | Class Templates options (Tools | IDE Options). - */ package com.intellij.codeInspection.redundantCast; import com.intellij.codeInsight.CodeInsightUtilBase; @@ -39,6 +30,10 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; +/** + * @author max + * Date: Dec 24, 2001 + */ public class RedundantCastInspection extends GenericsInspectionToolBase { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.redundantCast.RedundantCastInspection"); private final LocalQuickFix myQuickFixAction; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PolymorphicTypeCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PolymorphicTypeCast.java index 5ca925c9cee8..2da4725db253 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PolymorphicTypeCast.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PolymorphicTypeCast.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +@SuppressWarnings({"UnusedDeclaration"}) class C { // from http://download.java.net/jdk7/docs/api/java/lang/invoke/MethodHandle.html, "Usage examples" void m() throws Throwable { @@ -64,6 +66,10 @@ class C { mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); mh.invokeExact(System.out, "Hello, world."); // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V + + MethodHandle mh0 = lookup.findVirtual(String.class, "length", MethodType.methodType(int.class)); + MethodHandle mh1 = MethodHandles.convertArguments(mh0, MethodType.methodType(Integer.class, String.class)); + System.out.println((Integer) mh1.invokeExact("daddy")); } void unsupported() { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java index 40f5519e84c1..43fb0bf2fc58 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java @@ -21,6 +21,7 @@ import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.compiler.JavacQuirksInspection; import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection; import com.intellij.codeInspection.defUse.DefUseInspection; +import com.intellij.codeInspection.redundantCast.RedundantCastInspection; import com.intellij.codeInspection.reference.EntryPoint; import com.intellij.codeInspection.reference.RefElement; import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection; @@ -51,7 +52,8 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { return new LocalInspectionTool[]{ new UnusedSymbolLocalInspection(), new UncheckedWarningLocalInspection(), - new JavacQuirksInspection() + new JavacQuirksInspection(), + new RedundantCastInspection() }; } @@ -262,7 +264,6 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { } public void testPolymorphicTypeCast() throws Exception { - Object o = null; - doTest(false, false); + doTest(true, false); } } diff --git a/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java b/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java index 81b325e1fb05..e1e2133dd435 100644 --- a/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java +++ b/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * Created by IntelliJ IDEA. - * User: max - * Date: Mar 24, 2002 - * Time: 6:08:14 PM - * To change template for new class use - * Code Style | Class Templates options (Tools | IDE Options). - */ package com.intellij.psi.util; +import com.intellij.codeInsight.AnnotationUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; @@ -38,9 +30,15 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +/** + * @author max + * Date: Mar 24, 2002 + */ public class RedundantCastUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.redundantCast.RedundantCastUtil"); + private RedundantCastUtil() { } + @NotNull public static List getRedundantCastsInside(PsiElement where) { MyCollectingVisitor visitor = new MyCollectingVisitor(); @@ -217,19 +215,15 @@ public class RedundantCastUtil { final PsiType newReturnType = newResult.getSubstitutor().substitute(newTargetMethod.getReturnType()); final PsiType oldReturnType = resolveResult.getSubstitutor().substitute(targetMethod.getReturnType()); if (Comparing.equal(newReturnType, oldReturnType)) { - if (newTargetMethod.equals(targetMethod)) { - addToResults(typeCast); - } - else if ( - newTargetMethod.getSignature(newResult.getSubstitutor()).equals(targetMethod.getSignature(resolveResult.getSubstitutor())) && - !(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) && // see SCR11555, SCR14559 - areThrownExceptionsCompatible(targetMethod, newTargetMethod)) { //see IDEADEV-15170 + if (newTargetMethod.equals(targetMethod) || + (newTargetMethod.getSignature(newResult.getSubstitutor()).equals(targetMethod.getSignature(resolveResult.getSubstitutor())) && + !(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) && // see SCR11555, SCR14559 + areThrownExceptionsCompatible(targetMethod, newTargetMethod))) { addToResults(typeCast); } } } - catch (IncorrectOperationException e) { - } + catch (IncorrectOperationException ignore) { } } private static boolean areThrownExceptionsCompatible(final PsiMethod targetMethod, final PsiMethod newTargetMethod) { @@ -397,8 +391,7 @@ public class RedundantCastUtil { } } } - catch (IncorrectOperationException e) { - } + catch (IncorrectOperationException ignore) { } } }); return result.get().booleanValue(); @@ -407,9 +400,13 @@ public class RedundantCastUtil { public static boolean isTypeCastSemantical(PsiTypeCastExpression typeCast) { PsiExpression operand = typeCast.getOperand(); if (operand == null) return false; + + if (isInPolymorphicCall(typeCast)) return true; + PsiType opType = operand.getType(); PsiTypeElement typeElement = typeCast.getCastType(); if (typeElement == null) return false; + PsiType castType = typeElement.getType(); if (castType instanceof PsiPrimitiveType) { if (opType instanceof PsiPrimitiveType) { @@ -442,11 +439,41 @@ public class RedundantCastUtil { } return false; } - private static boolean wrapperCastChangeSemantics(PsiExpression operand, PsiExpression otherOperand, PsiExpression toCast) { - boolean isPrimitiveComparisonWithCast = TypeConversionUtil.isPrimitiveAndNotNull(operand.getType()) || TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType()); - boolean isPrimitiveComparisonWithoutCast = TypeConversionUtil.isPrimitiveAndNotNull(toCast.getType()) || TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType()); - // wrapper casted to primitive vs wrapper comparison + private static boolean wrapperCastChangeSemantics(PsiExpression operand, PsiExpression otherOperand, PsiExpression toCast) { + boolean isPrimitiveComparisonWithCast = TypeConversionUtil.isPrimitiveAndNotNull(operand.getType()) || + TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType()); + boolean isPrimitiveComparisonWithoutCast = TypeConversionUtil.isPrimitiveAndNotNull(toCast.getType()) || + TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType()); + // wrapper casted to primitive vs wrapper comparison return isPrimitiveComparisonWithCast != isPrimitiveComparisonWithoutCast; } + + // see http://download.java.net/jdk7/docs/api/java/lang/invoke/MethodHandle.html#sigpoly + public static boolean isInPolymorphicCall(final PsiTypeCastExpression typeCast) { + if (!PsiUtil.isLanguageLevel7OrHigher(typeCast)) return false; + + // return type + final PsiExpression operand = typeCast.getOperand(); + if (operand instanceof PsiMethodCallExpression) { + if (isPolymorphicMethod((PsiMethodCallExpression)operand)) return true; + } + + // argument type + final PsiElement exprList = typeCast.getParent(); + if (exprList instanceof PsiExpressionList) { + final PsiElement methodCall = exprList.getParent(); + if (methodCall instanceof PsiMethodCallExpression) { + if (isPolymorphicMethod((PsiMethodCallExpression)methodCall)) return true; + } + } + + return false; + } + + private static boolean isPolymorphicMethod(PsiMethodCallExpression expression) { + final PsiElement method = expression.getMethodExpression().resolve(); + return method instanceof PsiMethod && + AnnotationUtil.isAnnotated((PsiMethod)method, CommonClassNames.JAVA_LANG_INVOKE_MH_POLYMORPHIC, false, true); + } }