Snapshot f5ae6e3be7e12e1ef9e12f48fe3a674266288e4e from master branch of git://git.jetbrains.org/idea/community.git
Change-Id: I756af70fb2910aa2687e94e28338fb9727bce518
diff --git a/java/java-psi-api/java-psi-api.iml b/java/java-psi-api/java-psi-api.iml
index caff682..4256838 100644
--- a/java/java-psi-api/java-psi-api.iml
+++ b/java/java-psi-api/java-psi-api.iml
@@ -8,6 +8,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="core-api" exported="" />
+ <orderEntry type="module" module-name="projectModel-api" />
</component>
</module>
diff --git a/java/java-psi-api/src/com/intellij/openapi/module/EffectiveLanguageLevelUtil.java b/java/java-psi-api/src/com/intellij/openapi/module/EffectiveLanguageLevelUtil.java
new file mode 100644
index 0000000..9a0fb12
--- /dev/null
+++ b/java/java-psi-api/src/com/intellij/openapi/module/EffectiveLanguageLevelUtil.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2013 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.module;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.roots.LanguageLevelModuleExtension;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.pom.java.LanguageLevel;
+import org.jetbrains.annotations.NotNull;
+
+public class EffectiveLanguageLevelUtil {
+ @NotNull
+ public static LanguageLevel getEffectiveLanguageLevel(@NotNull final Module module) {
+ ApplicationManager.getApplication().assertReadAccessAllowed();
+ LanguageLevel level = LanguageLevelModuleExtension.getInstance(module).getLanguageLevel();
+ if (level != null) return level;
+ return LanguageLevelProjectExtension.getInstance(module.getProject()).getLanguageLevel();
+ }
+}
diff --git a/java/java-psi-api/src/com/intellij/openapi/projectRoots/JdkVersionUtil.java b/java/java-psi-api/src/com/intellij/openapi/projectRoots/JdkVersionUtil.java
new file mode 100644
index 0000000..d26b1ca
--- /dev/null
+++ b/java/java-psi-api/src/com/intellij/openapi/projectRoots/JdkVersionUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2013 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.projectRoots;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public class JdkVersionUtil {
+ private static final Map<JavaSdkVersion, String[]> VERSION_STRINGS = new EnumMap<JavaSdkVersion, String[]>(JavaSdkVersion.class);
+
+ static {
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_0, new String[]{"1.0"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_1, new String[]{"1.1"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_2, new String[]{"1.2"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_3, new String[]{"1.3"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_4, new String[]{"1.4"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_5, new String[]{"1.5", "5.0"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_6, new String[]{"1.6", "6.0"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_7, new String[]{"1.7", "7.0"});
+ VERSION_STRINGS.put(JavaSdkVersion.JDK_1_8, new String[]{"1.8", "8.0"});
+ }
+
+ public static JavaSdkVersion getVersion(@NotNull String versionString) {
+ for (Map.Entry<JavaSdkVersion, String[]> entry : VERSION_STRINGS.entrySet()) {
+ for (String s : entry.getValue()) {
+ if (versionString.contains(s)) {
+ return entry.getKey();
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/java-psi-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java b/java/java-psi-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java
new file mode 100644
index 0000000..5ede20d
--- /dev/null
+++ b/java/java-psi-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2009 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * User: anna
+ * Date: 27-Dec-2007
+ */
+package com.intellij.openapi.roots;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.pom.java.LanguageLevel;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+public class LanguageLevelModuleExtension extends ModuleExtension<LanguageLevelModuleExtension> {
+ @NonNls private static final String LANGUAGE_LEVEL_ELEMENT_NAME = "LANGUAGE_LEVEL";
+ private Module myModule;
+ private final boolean myWritable;
+ private static final Logger LOG = Logger.getInstance("#" + LanguageLevelModuleExtension.class.getName());
+
+ public static LanguageLevelModuleExtension getInstance(final Module module) {
+ return ModuleRootManager.getInstance(module).getModuleExtension(LanguageLevelModuleExtension.class);
+ }
+
+ private LanguageLevel myLanguageLevel;
+ private final LanguageLevelModuleExtension mySource;
+
+ public LanguageLevelModuleExtension(Module module) {
+ myModule = module;
+ mySource = null;
+ myWritable = false;
+ }
+
+ public LanguageLevelModuleExtension(final LanguageLevelModuleExtension source, boolean writable) {
+ myWritable = writable;
+ myModule = source.myModule;
+ myLanguageLevel = source.myLanguageLevel;
+ mySource = source;
+ }
+
+ public void setLanguageLevel(final LanguageLevel languageLevel) {
+ LOG.assertTrue(myWritable, "Writable model can be retrieved from writable ModifiableRootModel");
+ myLanguageLevel = languageLevel;
+ }
+
+ @Nullable
+ public LanguageLevel getLanguageLevel() {
+ return myLanguageLevel;
+ }
+
+ @Override
+ public void readExternal(final Element element) throws InvalidDataException {
+ final String languageLevel = element.getAttributeValue(LANGUAGE_LEVEL_ELEMENT_NAME);
+ if (languageLevel != null) {
+ try {
+ myLanguageLevel = LanguageLevel.valueOf(languageLevel);
+ }
+ catch (IllegalArgumentException e) {
+ //bad value was stored
+ }
+ }
+ }
+
+ @Override
+ public void writeExternal(final Element element) throws WriteExternalException {
+ if (myLanguageLevel != null) {
+ element.setAttribute(LANGUAGE_LEVEL_ELEMENT_NAME, myLanguageLevel.toString());
+ }
+ }
+
+ @Override
+ public ModuleExtension getModifiableModel(final boolean writable) {
+ return new LanguageLevelModuleExtension(this, writable);
+ }
+
+ @Override
+ public void commit() {
+ if (mySource != null && mySource.myLanguageLevel != myLanguageLevel) {
+ if (myModule.isLoaded()) { //do not reload project for non-committed modules: j2me|project imports
+ if (mySource.myLanguageLevel != myLanguageLevel) {
+ final LanguageLevelProjectExtension languageLevelProjectExtension =
+ LanguageLevelProjectExtension.getInstance(myModule.getProject());
+ final LanguageLevel projectLanguageLevel = languageLevelProjectExtension.getLanguageLevel();
+ final boolean explicit2ImplicitProjectLevel = myLanguageLevel == null && mySource.myLanguageLevel == projectLanguageLevel;
+ final boolean implicit2ExplicitProjectLevel = mySource.myLanguageLevel == null && myLanguageLevel == projectLanguageLevel;
+ if (!(explicit2ImplicitProjectLevel || implicit2ExplicitProjectLevel)) {
+ languageLevelProjectExtension.reloadProjectOnLanguageLevelChange(myLanguageLevel == null ? projectLanguageLevel : myLanguageLevel, true);
+ }
+ }
+ }
+ mySource.myLanguageLevel = myLanguageLevel;
+ }
+ }
+
+ @Override
+ public boolean isChanged() {
+ return mySource != null && mySource.myLanguageLevel != myLanguageLevel;
+ }
+
+ @Override
+ public void dispose() {
+ myModule = null;
+ myLanguageLevel = null;
+ }
+}
diff --git a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java
index 06d4037..f2a6236 100644
--- a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java
+++ b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java
@@ -385,6 +385,8 @@
final PsiType bound = ((PsiWildcardType)type).getBound();
return bound != null ? bound
: ((PsiWildcardType)type).getExtendsBound();//object
+ } else if (type instanceof PsiCapturedWildcardType && !eliminateInTypeArguments) {
+ return eliminateWildcards(((PsiCapturedWildcardType)type).getWildcard(), eliminateInTypeArguments);
}
return type;
}
diff --git a/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java b/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java
new file mode 100644
index 0000000..2852584
--- /dev/null
+++ b/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2000-2013 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+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;
+import com.intellij.psi.*;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+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<PsiTypeCastExpression> getRedundantCastsInside(PsiElement where) {
+ MyCollectingVisitor visitor = new MyCollectingVisitor();
+ if (where instanceof PsiEnumConstant) {
+ where.accept(visitor);
+ } else {
+ where.acceptChildren(visitor);
+ }
+ return new ArrayList<PsiTypeCastExpression>(visitor.myFoundCasts);
+ }
+
+ public static boolean isCastRedundant (PsiTypeCastExpression typeCast) {
+ PsiElement parent = typeCast.getParent();
+ while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
+ if (parent instanceof PsiExpressionList) parent = parent.getParent();
+ if (parent instanceof PsiReferenceExpression) parent = parent.getParent();
+ MyIsRedundantVisitor visitor = new MyIsRedundantVisitor(false);
+ parent.accept(visitor);
+ return visitor.isRedundant;
+ }
+
+ @Nullable
+ private static PsiExpression deparenthesizeExpression(PsiExpression arg) {
+ while (arg instanceof PsiParenthesizedExpression) arg = ((PsiParenthesizedExpression) arg).getExpression();
+ return arg;
+ }
+
+ public static void removeCast(PsiTypeCastExpression castExpression) {
+ if (castExpression == null) return;
+ PsiExpression operand = castExpression.getOperand();
+ if (operand instanceof PsiParenthesizedExpression) {
+ final PsiParenthesizedExpression parExpr = (PsiParenthesizedExpression)operand;
+ operand = parExpr.getExpression();
+ }
+ if (operand == null) return;
+
+ PsiElement toBeReplaced = castExpression;
+
+ PsiElement parent = castExpression.getParent();
+ while (parent instanceof PsiParenthesizedExpression) {
+ toBeReplaced = parent;
+ parent = parent.getParent();
+ }
+
+ try {
+ toBeReplaced.replace(operand);
+ }
+ catch (IncorrectOperationException e) {
+ LOG.error(e);
+ }
+ }
+
+ private static class MyCollectingVisitor extends MyIsRedundantVisitor {
+ private final Set<PsiTypeCastExpression> myFoundCasts = new HashSet<PsiTypeCastExpression>();
+
+ private MyCollectingVisitor() {
+ super(true);
+ }
+
+ @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
+ expression.acceptChildren(this);
+ }
+
+ @Override public void visitClass(PsiClass aClass) {
+ // avoid multiple visit
+ }
+
+ @Override public void visitMethod(PsiMethod method) {
+ // avoid multiple visit
+ }
+
+ @Override public void visitField(PsiField field) {
+ // avoid multiple visit
+ }
+
+ @Override
+ protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
+ if (!isTypeCastSemantic(typeCast)) {
+ myFoundCasts.add(typeCast);
+ }
+ }
+ }
+
+ private static class MyIsRedundantVisitor extends JavaRecursiveElementVisitor {
+ private boolean isRedundant = false;
+ private final boolean myRecursive;
+
+ private MyIsRedundantVisitor(final boolean recursive) {
+ myRecursive = recursive;
+ }
+
+ @Override
+ public void visitElement(final PsiElement element) {
+ if (myRecursive) {
+ super.visitElement(element);
+ }
+ }
+
+ protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
+ if (!isTypeCastSemantic(typeCast)) {
+ isRedundant = true;
+ }
+ }
+
+ @Override public void visitAssignmentExpression(PsiAssignmentExpression expression) {
+ processPossibleTypeCast(expression.getRExpression(), expression.getLExpression().getType());
+ super.visitAssignmentExpression(expression);
+ }
+
+ @Override public void visitVariable(PsiVariable variable) {
+ processPossibleTypeCast(variable.getInitializer(), variable.getType());
+ super.visitVariable(variable);
+ }
+
+ @Override public void visitReturnStatement(PsiReturnStatement statement) {
+ final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
+ if (method != null) {
+ final PsiType returnType = method.getReturnType();
+ final PsiExpression returnValue = statement.getReturnValue();
+ if (returnValue != null) {
+ processPossibleTypeCast(returnValue, returnType);
+ }
+ }
+ super.visitReturnStatement(statement);
+ }
+
+ @Override
+ public void visitPolyadicExpression(PsiPolyadicExpression expression) {
+ IElementType tokenType = expression.getOperationTokenType();
+ PsiExpression[] operands = expression.getOperands();
+ if (operands.length >= 2) {
+ PsiType lType = operands[0].getType();
+ processBinaryExpressionOperand(deparenthesizeExpression(operands[0]), operands[1].getType(), tokenType);
+ for (int i = 1; i < operands.length; i++) {
+ PsiExpression operand = deparenthesizeExpression(operands[i]);
+ if (operand == null) continue;
+ processBinaryExpressionOperand(operand, lType, tokenType);
+ lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, operand.getType(), tokenType, true);
+ }
+ }
+ super.visitPolyadicExpression(expression);
+ }
+
+ private void processBinaryExpressionOperand(final PsiExpression operand,
+ final PsiType otherType,
+ final IElementType binaryToken) {
+ if (operand instanceof PsiTypeCastExpression) {
+ PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
+ PsiExpression toCast = typeCast.getOperand();
+ if (toCast != null && TypeConversionUtil.isBinaryOperatorApplicable(binaryToken, toCast.getType(), otherType, false)) {
+ addToResults(typeCast);
+ }
+ }
+ }
+
+ private void processPossibleTypeCast(PsiExpression rExpr, @Nullable PsiType lType) {
+ rExpr = deparenthesizeExpression(rExpr);
+ if (rExpr instanceof PsiTypeCastExpression) {
+ PsiExpression castOperand = ((PsiTypeCastExpression)rExpr).getOperand();
+ if (castOperand != null) {
+ PsiType operandType = castOperand.getType();
+ if (operandType != null) {
+ if (lType != null && TypeConversionUtil.isAssignable(lType, operandType, false)) {
+ addToResults((PsiTypeCastExpression)rExpr);
+ }
+ }
+ }
+ }
+ }
+
+ @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+ processCall(expression);
+
+ checkForVirtual(expression);
+ super.visitMethodCallExpression(expression);
+ }
+
+ private void checkForVirtual(PsiMethodCallExpression methodCall) {
+ PsiReferenceExpression methodExpr = methodCall.getMethodExpression();
+ PsiExpression qualifier = methodExpr.getQualifierExpression();
+ if (!(qualifier instanceof PsiParenthesizedExpression)) return;
+ PsiExpression operand = ((PsiParenthesizedExpression)qualifier).getExpression();
+ if (!(operand instanceof PsiTypeCastExpression)) return;
+ PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
+ PsiExpression castOperand = typeCast.getOperand();
+ if (castOperand == null) return;
+
+ PsiType type = castOperand.getType();
+ if (type == null) return;
+ if (type instanceof PsiPrimitiveType) return;
+
+ final JavaResolveResult resolveResult = methodExpr.advancedResolve(false);
+ PsiMethod targetMethod = (PsiMethod)resolveResult.getElement();
+ if (targetMethod == null) return;
+ if (targetMethod.hasModifierProperty(PsiModifier.STATIC)) return;
+
+ try {
+ PsiManager manager = methodExpr.getManager();
+ PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
+
+ final PsiExpression expressionFromText = factory.createExpressionFromText(methodCall.getText(), methodCall);
+ if (!(expressionFromText instanceof PsiMethodCallExpression)) return;
+ PsiMethodCallExpression newCall = (PsiMethodCallExpression)expressionFromText;
+ PsiExpression newQualifier = newCall.getMethodExpression().getQualifierExpression();
+ PsiExpression newOperand = ((PsiTypeCastExpression)((PsiParenthesizedExpression)newQualifier).getExpression()).getOperand();
+ newQualifier.replace(newOperand);
+
+ final JavaResolveResult newResult = newCall.getMethodExpression().advancedResolve(false);
+ if (!newResult.isValidResult()) return;
+ final PsiMethod newTargetMethod = (PsiMethod)newResult.getElement();
+ 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) ||
+ (newTargetMethod.getSignature(newResult.getSubstitutor()).equals(targetMethod.getSignature(resolveResult.getSubstitutor())) &&
+ !(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) && // see SCR11555, SCR14559
+ areThrownExceptionsCompatible(targetMethod, newTargetMethod))) {
+ addToResults(typeCast);
+ }
+ }
+ }
+ catch (IncorrectOperationException ignore) { }
+ }
+
+ private static boolean areThrownExceptionsCompatible(final PsiMethod targetMethod, final PsiMethod newTargetMethod) {
+ final PsiClassType[] oldThrowsTypes = targetMethod.getThrowsList().getReferencedTypes();
+ final PsiClassType[] newThrowsTypes = newTargetMethod.getThrowsList().getReferencedTypes();
+ for (final PsiClassType throwsType : newThrowsTypes) {
+ if (!isExceptionThrown(throwsType, oldThrowsTypes)) return false;
+ }
+ return true;
+ }
+
+ private static boolean isExceptionThrown(PsiClassType exceptionType, PsiClassType[] thrownTypes) {
+ for (final PsiClassType type : thrownTypes) {
+ if (type.equals(exceptionType)) return true;
+ }
+ return false;
+ }
+
+ @Override public void visitNewExpression(PsiNewExpression expression) {
+ processCall(expression);
+ super.visitNewExpression(expression);
+ }
+
+ @Override
+ public void visitEnumConstant(PsiEnumConstant enumConstant) {
+ processCall(enumConstant);
+ super.visitEnumConstant(enumConstant);
+ }
+
+ @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
+ //expression.acceptChildren(this);
+ }
+
+ private void processCall(PsiCall expression){
+ PsiExpressionList argumentList = expression.getArgumentList();
+ if (argumentList == null) return;
+ PsiExpression[] args = argumentList.getExpressions();
+ PsiMethod oldMethod = expression.resolveMethod();
+ if (oldMethod == null) return;
+ PsiParameter[] parameters = oldMethod.getParameterList().getParameters();
+
+ try {
+ for (int i = 0; i < args.length; i++) {
+ final PsiExpression arg = deparenthesizeExpression(args[i]);
+ if (arg instanceof PsiTypeCastExpression) {
+ PsiTypeCastExpression cast = (PsiTypeCastExpression)arg;
+ if (i == args.length - 1 && args.length == parameters.length && parameters[i].isVarArgs()) {
+ //do not mark cast to resolve ambiguity for calling varargs method with inexact argument
+ continue;
+ }
+ PsiCall newCall = (PsiCall) expression.copy();
+ final PsiExpressionList argList = newCall.getArgumentList();
+ LOG.assertTrue(argList != null);
+ PsiExpression[] newArgs = argList.getExpressions();
+ PsiTypeCastExpression castExpression = (PsiTypeCastExpression) deparenthesizeExpression(newArgs[i]);
+ PsiExpression castOperand = castExpression.getOperand();
+ if (castOperand == null) return;
+ castExpression.replace(castOperand);
+ if (newCall instanceof PsiEnumConstant) {
+ // do this manually, because PsiEnumConstantImpl.resolveMethodGenerics() will assert (no containing class for the copy)
+ final PsiEnumConstant enumConstant = (PsiEnumConstant)expression;
+ PsiClass containingClass = enumConstant.getContainingClass();
+ final JavaPsiFacade facade = JavaPsiFacade.getInstance(enumConstant.getProject());
+ final PsiClassType type = facade.getElementFactory().createType(containingClass);
+ final JavaResolveResult newResult = facade.getResolveHelper().resolveConstructor(type, newCall.getArgumentList(), enumConstant);
+ if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult()) {
+ addToResults(cast);
+ }
+ } else {
+ final JavaResolveResult newResult = newCall.resolveMethodGenerics();
+ if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult() &&
+ Comparing.equal(((PsiCallExpression)newCall).getType(), ((PsiCallExpression)expression).getType())) {
+ addToResults(cast);
+ }
+ }
+ }
+ }
+ }
+ catch (IncorrectOperationException e) {
+ return;
+ }
+
+ for (PsiExpression arg : args) {
+ if (arg instanceof PsiTypeCastExpression) {
+ PsiExpression castOperand = ((PsiTypeCastExpression)arg).getOperand();
+ if (castOperand != null) {
+ castOperand.accept(this);
+ }
+ }
+ else {
+ arg.accept(this);
+ }
+ }
+ }
+
+ @Override public void visitTypeCastExpression(PsiTypeCastExpression typeCast) {
+ PsiExpression operand = typeCast.getOperand();
+ if (operand == null) return;
+
+ PsiElement expr = deparenthesizeExpression(operand);
+
+ final PsiType topCastType = typeCast.getType();
+ if (expr instanceof PsiTypeCastExpression) {
+ PsiTypeElement typeElement = ((PsiTypeCastExpression)expr).getCastType();
+ if (typeElement == null) return;
+ PsiType castType = typeElement.getType();
+ final PsiExpression innerOperand = ((PsiTypeCastExpression)expr).getOperand();
+ final PsiType operandType = innerOperand != null ? innerOperand.getType() : null;
+ if (!(castType instanceof PsiPrimitiveType)) {
+ if (operandType != null && topCastType != null && TypeConversionUtil.areTypesConvertible(operandType, topCastType)) {
+ addToResults((PsiTypeCastExpression)expr);
+ }
+ } else if (PsiPrimitiveType.getUnboxedType(operandType) == topCastType) {
+ addToResults((PsiTypeCastExpression)expr);
+ }
+ }
+ else {
+ PsiElement parent = typeCast.getParent();
+ if (parent instanceof PsiConditionalExpression) {
+ //branches need to be of the same type
+ final PsiType operandType = operand.getType();
+ final PsiType conditionalType = ((PsiConditionalExpression)parent).getType();
+ if (!Comparing.equal(operandType, conditionalType)) {
+ if (!PsiUtil.isLanguageLevel5OrHigher(typeCast)) {
+ return;
+ }
+ if (!checkResolveAfterRemoveCast(parent)) return;
+ }
+ } else if (parent instanceof PsiSynchronizedStatement && (expr instanceof PsiExpression && ((PsiExpression)expr).getType() instanceof PsiPrimitiveType)) {
+ return;
+ } else if (expr instanceof PsiLambdaExpression || expr instanceof PsiMethodReferenceExpression) {
+ if (parent instanceof PsiParenthesizedExpression && parent.getParent() instanceof PsiReferenceExpression) {
+ return;
+ }
+
+ final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(typeCast, true);
+ if (topCastType != null && functionalInterfaceType != null && !TypeConversionUtil.isAssignable(topCastType, functionalInterfaceType, false)) return;
+ }
+ processAlreadyHasTypeCast(typeCast);
+ }
+ super.visitTypeCastExpression(typeCast);
+ }
+
+ private static boolean checkResolveAfterRemoveCast(PsiElement parent) {
+ PsiElement grandPa = parent.getParent();
+ if (grandPa instanceof PsiExpressionList) {
+ PsiExpression[] expressions = ((PsiExpressionList)grandPa).getExpressions();
+ int idx = ArrayUtil.find(expressions, parent);
+ PsiElement grandGrandPa = grandPa.getParent();
+ if (grandGrandPa instanceof PsiCall) {
+ PsiElement resolve = ((PsiCall)grandGrandPa).resolveMethod();
+ if (resolve instanceof PsiMethod) {
+ PsiCall expression = (PsiCall)grandGrandPa.copy();
+ PsiExpressionList argumentList = expression.getArgumentList();
+ LOG.assertTrue(argumentList != null);
+ PsiExpression toReplace = argumentList.getExpressions()[idx];
+ if (toReplace instanceof PsiConditionalExpression) {
+ PsiExpression thenExpression = ((PsiConditionalExpression)toReplace).getThenExpression();
+ PsiExpression elseExpression = ((PsiConditionalExpression)toReplace).getElseExpression();
+ if (thenExpression instanceof PsiTypeCastExpression) {
+ final PsiExpression thenOperand = ((PsiTypeCastExpression)thenExpression).getOperand();
+ if (thenOperand != null) {
+ thenExpression.replace(thenOperand);
+ }
+ } else if (elseExpression instanceof PsiTypeCastExpression) {
+ final PsiExpression elseOperand = ((PsiTypeCastExpression)elseExpression).getOperand();
+ if (elseOperand != null) {
+ elseExpression.replace(elseOperand);
+ }
+ }
+ }
+ if (expression.resolveMethod() != resolve) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private void processAlreadyHasTypeCast(PsiTypeCastExpression typeCast){
+ PsiElement parent = typeCast.getParent();
+ while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
+ if (parent instanceof PsiExpressionList) return; // do not replace in arg lists - should be handled by parent
+ if (parent instanceof PsiReturnStatement) return;
+ if (parent instanceof PsiTypeCastExpression) return;
+
+ if (isTypeCastSemantic(typeCast)) return;
+
+ PsiTypeElement typeElement = typeCast.getCastType();
+ if (typeElement == null) return;
+ final PsiType castTo = typeElement.getType();
+ final PsiType opType = typeCast.getOperand().getType();
+ if (opType == null) return;
+ if (parent instanceof PsiReferenceExpression) {
+ if (castTo instanceof PsiClassType && opType instanceof PsiPrimitiveType) return; //explicit boxing
+ //Check accessibility
+ if (opType instanceof PsiClassType) {
+ final PsiReferenceExpression refExpression = (PsiReferenceExpression)parent;
+ PsiElement element = refExpression.resolve();
+ if (!(element instanceof PsiMember)) return;
+ PsiClass accessClass = ((PsiClassType)opType).resolve();
+ if (accessClass == null) return;
+ if (!JavaPsiFacade.getInstance(parent.getProject()).getResolveHelper().isAccessible((PsiMember)element, typeCast, accessClass)) return;
+ if (!isCastRedundantInRefExpression(refExpression, typeCast.getOperand())) return;
+ }
+ }
+
+ if (arrayAccessAtTheLeftSideOfAssignment(parent)) {
+ if (TypeConversionUtil.isAssignable(opType, castTo, false) && opType.getArrayDimensions() == castTo.getArrayDimensions()) {
+ addToResults(typeCast);
+ }
+ }
+ else {
+ if (TypeConversionUtil.isAssignable(castTo, opType, false)) {
+ addToResults(typeCast);
+ }
+ }
+ }
+
+ private static boolean arrayAccessAtTheLeftSideOfAssignment(PsiElement element) {
+ PsiAssignmentExpression assignment = PsiTreeUtil.getParentOfType(element, PsiAssignmentExpression.class, false, PsiMember.class);
+ if (assignment == null) return false;
+ PsiExpression lExpression = assignment.getLExpression();
+ return PsiTreeUtil.isAncestor(lExpression, element, false) && lExpression instanceof PsiArrayAccessExpression;
+ }
+ }
+
+ private static boolean isCastRedundantInRefExpression (final PsiReferenceExpression refExpression, final PsiExpression castOperand) {
+ final PsiElement resolved = refExpression.resolve();
+ final Ref<Boolean> result = new Ref<Boolean>(Boolean.FALSE);
+ CodeStyleManager.getInstance(refExpression.getProject()).performActionWithFormatterDisabled(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(refExpression.getProject()).getElementFactory();
+ final PsiExpression copyExpression = elementFactory.createExpressionFromText(refExpression.getText(), refExpression);
+ if (copyExpression instanceof PsiReferenceExpression) {
+ final PsiReferenceExpression copy = (PsiReferenceExpression)copyExpression;
+ final PsiExpression qualifier = copy.getQualifierExpression();
+ if (qualifier != null) {
+ qualifier.replace(castOperand);
+ result.set(Boolean.valueOf(copy.resolve() == resolved));
+ }
+ }
+ }
+ catch (IncorrectOperationException ignore) { }
+ }
+ });
+ return result.get().booleanValue();
+ }
+
+ /** @deprecated use {@link #isTypeCastSemantic(PsiTypeCastExpression)} (to remove in IDEA 14) */
+ @SuppressWarnings({"UnusedDeclaration", "SpellCheckingInspection"})
+ public static boolean isTypeCastSemantical(PsiTypeCastExpression typeCast) {
+ return isTypeCastSemantic(typeCast);
+ }
+
+ public static boolean isTypeCastSemantic(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) {
+ return !opType.equals(castType); // let's suppose all not equal primitive casts are necessary
+ }
+ final PsiPrimitiveType unboxedOpType = PsiPrimitiveType.getUnboxedType(opType);
+ if (unboxedOpType != null && !unboxedOpType.equals(castType) ) {
+ return true;
+ }
+ }
+ else if (castType instanceof PsiClassType && ((PsiClassType)castType).hasParameters()) {
+ if (opType instanceof PsiClassType && ((PsiClassType)opType).isRaw()) return true;
+ } else if (castType instanceof PsiClassType && ((PsiClassType)castType).isRaw()) {
+ if (opType instanceof PsiClassType && ((PsiClassType)opType).hasParameters()) return true;
+ }
+
+ if (operand instanceof PsiLambdaExpression || operand instanceof PsiMethodReferenceExpression) {
+ if (castType instanceof PsiClassType && InheritanceUtil.isInheritor(PsiUtil.resolveClassInType(castType), CommonClassNames.JAVA_IO_SERIALIZABLE)) return true;
+ if (castType instanceof PsiIntersectionType) {
+ boolean redundant = false;
+ final PsiType[] conjuncts = ((PsiIntersectionType)castType).getConjuncts();
+ for (int i = 1; i < conjuncts.length; i++) {
+ PsiType conjunct = conjuncts[i];
+ if (TypeConversionUtil.isAssignable(conjuncts[0], conjunct)) {
+ redundant = true;
+ break;
+ }
+ }
+ if (!redundant) {
+ return true;
+ }
+ }
+ }
+
+ PsiElement parent = typeCast.getParent();
+ while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
+
+ if (parent instanceof PsiBinaryExpression) {
+ PsiBinaryExpression expression = (PsiBinaryExpression)parent;
+ PsiExpression firstOperand = expression.getLOperand();
+ PsiExpression otherOperand = expression.getROperand();
+ if (PsiTreeUtil.isAncestor(otherOperand, typeCast, false)) {
+ PsiExpression temp = otherOperand;
+ otherOperand = firstOperand;
+ firstOperand = temp;
+ }
+ if (firstOperand != null && otherOperand != null && wrapperCastChangeSemantics(firstOperand, otherOperand, operand)) {
+ return true;
+ }
+ } else if (parent instanceof PsiConditionalExpression) {
+ if (opType instanceof PsiPrimitiveType && !(((PsiConditionalExpression)parent).getType() instanceof PsiPrimitiveType)) {
+ if (PsiPrimitiveType.getUnboxedType(PsiTypesUtil.getExpectedTypeByParent((PsiExpression)parent)) != null) {
+ return true;
+ }
+ }
+ }
+ 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
+ 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);
+ }
+}