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);
+  }
+}