warn about compilable code which would lead to runtime ClassCastException in addition to unchecked warning which nobody understands (IDEA-154345)

This commit is contained in:
Anna.Kozlova
2016-04-15 18:43:24 +02:00
parent 24fbcb7563
commit bb6394162a
3 changed files with 46 additions and 2 deletions
@@ -31,6 +31,7 @@ import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.PsiReplacementUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
@@ -82,12 +83,16 @@ public class JavacQuirksInspectionVisitor extends JavaElementVisitor {
public void visitAssignmentExpression(PsiAssignmentExpression assignment) {
super.visitAssignmentExpression(assignment);
final PsiType lType = assignment.getLExpression().getType();
if (lType == null) return;
final PsiExpression rExpression = assignment.getRExpression();
if (rExpression == null) return;
PsiJavaToken operationSign = assignment.getOperationSign();
checkIntersectionType(lType, rExpression.getType(), operationSign);
IElementType eqOpSign = operationSign.getTokenType();
IElementType opSign = TypeConversionUtil.convertEQtoOperation(eqOpSign);
if (opSign == null) return;
final PsiExpression rExpression = assignment.getRExpression();
if (rExpression == null) return;
if (JavaSdkVersion.JDK_1_6.equals(JavaVersionService.getInstance().getJavaSdkVersion(assignment)) &&
PsiType.getJavaLangObject(assignment.getManager(), assignment.getResolveScope()).equals(lType)) {
String operatorText = operationSign.getText().substring(0, operationSign.getText().length() - 1);
@@ -100,6 +105,34 @@ public class JavacQuirksInspectionVisitor extends JavaElementVisitor {
}
}
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
final PsiExpression initializer = variable.getInitializer();
if (initializer != null) {
final PsiElement assignmentToken = PsiTreeUtil.skipSiblingsBackward(initializer, PsiWhiteSpace.class);
if (assignmentToken != null) {
checkIntersectionType(variable.getType(), initializer.getType(), assignmentToken);
}
}
}
private void checkIntersectionType(@NotNull PsiType lType, @Nullable PsiType rType, @NotNull PsiElement elementToHighlight) {
if (rType instanceof PsiIntersectionType && TypeConversionUtil.isAssignable(lType, rType)) {
final PsiClass psiClass = PsiUtil.resolveClassInType(lType);
if (psiClass != null && psiClass.hasModifierProperty(PsiModifier.FINAL)) {
final PsiType[] conjuncts = ((PsiIntersectionType)rType).getConjuncts();
for (PsiType conjunct : conjuncts) {
if (!TypeConversionUtil.isAssignable(conjunct, lType)) {
final String descriptionTemplate =
"Though assignment is formal correct, it could lead to ClassCastException at runtime. Expected: '" + lType.getPresentableText() + "', actual: '" + rType.getPresentableText() + "'";
myHolder.registerProblem(elementToHighlight, descriptionTemplate);
}
}
}
}
}
@Override
public void visitIdentifier(PsiIdentifier identifier) {
super.visitIdentifier(identifier);
@@ -0,0 +1,10 @@
class Test {
<S extends Runnable> S f() {
return null;
}
{
String m <warning descr="Though assignment is formal correct, it could lead to ClassCastException at runtime. Expected: 'String', actual: 'Runnable & String'">=</warning> f();
System.out.println(m);
}
}
@@ -168,4 +168,5 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase {
public void testAccessToStaticMethodsFromInterfaces() { doTest(true, false); }
public void testUncheckedExtendedWarnings() { doTest(true, false); }
public void testInaccessibleInferredTypeForVarargsArgument() { doTest(false, false);}
public void testRuntimeClassCast() { doTest(true, false);}
}