unchecked warnings as fair inspection

This commit is contained in:
anna
2011-02-17 09:48:44 +01:00
parent b002ef196e
commit 84e7a9ae7a
13 changed files with 393 additions and 248 deletions
@@ -16,29 +16,18 @@
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.JavaHightlightInfoTypes;
import com.intellij.codeInsight.daemon.impl.actions.SuppressFix;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.intention.EmptyIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.SuppressManager;
import com.intellij.codeInspection.ex.InspectionManagerEx;
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper;
import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -48,7 +37,6 @@ import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -529,21 +517,7 @@ public class GenericsHighlightUtil {
return null;
}
//precondition: TypeConversionUtil.isAssignable(lType, rType) || expressionAssignable
public static HighlightInfo checkRawToGenericAssignment(PsiType lType, PsiType rType, @NotNull final PsiElement elementToHighlight) {
if (!PsiUtil.isLanguageLevel5OrHigher(elementToHighlight)) return null;
final HighlightDisplayKey key = HighlightDisplayKey.find(UncheckedWarningLocalInspection.SHORT_NAME);
if (!InspectionProjectProfileManager.getInstance(elementToHighlight.getProject()).getInspectionProfile().isToolEnabled(key,
elementToHighlight)) return null;
if (!isRawToGeneric(lType, rType)) return null;
String description = JavaErrorMessages.message("generics.unchecked.assignment",
HighlightUtil.formatType(rType),
HighlightUtil.formatType(lType));
return createUncheckedWarning(elementToHighlight, key, description, elementToHighlight);
}
private static boolean isRawToGeneric(PsiType lType, PsiType rType) {
public static boolean isRawToGeneric(PsiType lType, PsiType rType) {
if (lType instanceof PsiPrimitiveType || rType instanceof PsiPrimitiveType) return false;
if (lType.equals(rType)) return false;
if (lType instanceof PsiArrayType && rType instanceof PsiArrayType) {
@@ -628,27 +602,7 @@ public class GenericsHighlightUtil {
return false;
}
public static HighlightInfo checkUncheckedTypeCast(PsiTypeCastExpression typeCast) {
if (!PsiUtil.isLanguageLevel5OrHigher(typeCast)) return null;
final HighlightDisplayKey key = HighlightDisplayKey.find(UncheckedWarningLocalInspection.SHORT_NAME);
if (!InspectionProjectProfileManager.getInstance(typeCast.getProject()).getInspectionProfile().isToolEnabled(key, typeCast)) return null;
final PsiTypeElement typeElement = typeCast.getCastType();
if (typeElement == null) return null;
final PsiType castType = typeElement.getType();
final PsiExpression expression = typeCast.getOperand();
if (expression == null) return null;
final PsiType exprType = expression.getType();
if (exprType == null) return null;
if (isUncheckedCast(castType, exprType)) {
String description = JavaErrorMessages.message("generics.unchecked.cast",
HighlightUtil.formatType(exprType),
HighlightUtil.formatType(castType));
return createUncheckedWarning(expression, key, description, typeCast);
}
return null;
}
private static boolean isUncheckedCast(PsiType castType, PsiType operandType) {
public static boolean isUncheckedCast(PsiType castType, PsiType operandType) {
if (TypeConversionUtil.isAssignable(castType, operandType, false)) return false;
castType = castType.getDeepComponentType();
@@ -727,74 +681,7 @@ public class GenericsHighlightUtil {
((PsiClassType)rTypeArg).resolve() instanceof PsiTypeParameter;
}
public static HighlightInfo checkUncheckedCall(JavaResolveResult resolveResult, PsiCall call) {
if (!PsiUtil.isLanguageLevel5OrHigher(call)) return null;
final HighlightDisplayKey key = HighlightDisplayKey.find(UncheckedWarningLocalInspection.SHORT_NAME);
if (!InspectionProjectProfileManager.getInstance(call.getProject()).getInspectionProfile().isToolEnabled(key, call)) return null;
final PsiMethod method = (PsiMethod)resolveResult.getElement();
if (method == null) return null;
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
final PsiParameter[] parameters = method.getParameterList().getParameters();
for (final PsiParameter parameter : parameters) {
final PsiType parameterType = parameter.getType();
if (parameterType.accept(new PsiTypeVisitor<Boolean>() {
public Boolean visitPrimitiveType(PsiPrimitiveType primitiveType) {
return Boolean.FALSE;
}
public Boolean visitArrayType(PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
public Boolean visitClassType(PsiClassType classType) {
PsiClass psiClass = classType.resolve();
if (psiClass instanceof PsiTypeParameter) {
return substitutor.substitute((PsiTypeParameter)psiClass) == null ? Boolean.TRUE : Boolean.FALSE;
}
PsiType[] parameters = classType.getParameters();
for (PsiType parameter : parameters) {
if (parameter.accept(this).booleanValue()) return Boolean.TRUE;
}
return Boolean.FALSE;
}
public Boolean visitWildcardType(PsiWildcardType wildcardType) {
PsiType bound = wildcardType.getBound();
if (bound != null) return bound.accept(this);
return Boolean.FALSE;
}
public Boolean visitEllipsisType(PsiEllipsisType ellipsisType) {
return ellipsisType.getComponentType().accept(this);
}
}).booleanValue()) {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
PsiType type = elementFactory.createType(method.getContainingClass(), substitutor);
String description = JavaErrorMessages.message("generics.unchecked.call.to.member.of.raw.type",
HighlightUtil.formatMethod(method),
HighlightUtil.formatType(type));
PsiElement element = call instanceof PsiMethodCallExpression
? ((PsiMethodCallExpression)call).getMethodExpression()
: call;
return createUncheckedWarning(call, key, description, element);
}
}
return null;
}
private static HighlightInfo createUncheckedWarning(PsiElement context, HighlightDisplayKey key, String description, PsiElement elementToHighlight) {
final InspectionProfile inspectionProfile =
InspectionProjectProfileManager.getInstance(context.getProject()).getInspectionProfile();
final LocalInspectionTool tool =
((LocalInspectionToolWrapper)inspectionProfile.getInspectionTool(UncheckedWarningLocalInspection.SHORT_NAME, elementToHighlight)).getTool();
if (InspectionManagerEx.inspectionResultSuppressed(context, tool)) return null;
HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(JavaHightlightInfoTypes.UNCHECKED_WARNING, elementToHighlight, description);
QuickFixAction.registerQuickFixAction(highlightInfo, new GenerifyFileFix(elementToHighlight.getContainingFile()), key);
return highlightInfo;
}
@Nullable
public static HighlightInfo checkForeachLoopParameterType(PsiForeachStatement statement) {
final PsiParameter parameter = statement.getIterationParameter();
final PsiExpression expression = statement.getIteratedValue();
@@ -811,14 +698,12 @@ public class GenericsHighlightUtil {
HighlightInfo highlightInfo = HighlightUtil.checkAssignability(parameterType, itemType, null, new TextRange(start, end));
if (highlightInfo != null) {
HighlightUtil.registerChangeVariableTypeFixes(parameter, itemType, highlightInfo);
} else {
highlightInfo = checkRawToGenericAssignment(parameterType, itemType, statement.getIterationParameter());
}
return highlightInfo;
}
@Nullable
private static PsiType getCollectionItemType(PsiExpression expression) {
public static PsiType getCollectionItemType(PsiExpression expression) {
final PsiType type = expression.getType();
if (type == null) return null;
if (type instanceof PsiArrayType) {
@@ -1112,27 +997,11 @@ public class GenericsHighlightUtil {
}
}
@Nullable
public static HighlightInfo checkUncheckedGenericsArrayCreation(PsiJavaCodeReferenceElement referenceExpression, PsiElement resolved){
final HighlightDisplayKey key = HighlightDisplayKey.find(UncheckedWarningLocalInspection.SHORT_NAME);
if (!InspectionProjectProfileManager.getInstance(referenceExpression.getProject()).getInspectionProfile().isToolEnabled(key, referenceExpression)) return null;
if (isUncheckedWarning(referenceExpression, resolved, false)) {
final HighlightInfo highlightInfo =
HighlightInfo.createHighlightInfo(HighlightInfoType.WARNING, referenceExpression, "Unchecked generics array creation for varargs parameter");
QuickFixAction.registerQuickFixAction(highlightInfo, new SuppressFix("unchecked"));
return highlightInfo;
}
return null;
}
public static boolean isUncheckedWarning(PsiJavaCodeReferenceElement expression, PsiElement resolve, boolean ignoreSuppressed) {
public static boolean isUncheckedWarning(PsiJavaCodeReferenceElement expression, PsiElement resolve) {
if (resolve instanceof PsiMethod) {
final PsiMethod psiMethod = (PsiMethod)resolve;
final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(expression);
if (!ignoreSuppressed) {
if (SuppressManager.getInstance().isSuppressedFor(expression, "unchecked")) return false;
}
if (psiMethod.isVarArgs()) {
if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_7) || !AnnotationUtil.isAnnotated(psiMethod, "java.lang.SafeVarargs", false)) {
@@ -1282,29 +1151,6 @@ public class GenericsHighlightUtil {
return list;
}
public static HighlightInfo checkGenericCallWithRawArguments(JavaResolveResult resolveResult, PsiCallExpression callExpression) {
final PsiMethod method = (PsiMethod)resolveResult.getElement();
if (method == null) return null;
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
final PsiExpressionList argumentList = callExpression.getArgumentList();
if (argumentList == null) return null;
final PsiExpression[] expressions = argumentList.getExpressions();
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != 0) {
for (int i = 0; i < expressions.length; i++) {
PsiParameter parameter = parameters[Math.min(i, parameters.length - 1)];
final PsiExpression expression = expressions[i];
final PsiType parameterType = substitutor.substitute(parameter.getType());
final PsiType expressionType = substitutor.substitute(expression.getType());
if (expressionType != null) {
final HighlightInfo highlightInfo = checkRawToGenericAssignment(parameterType, expressionType, expression);
if (highlightInfo != null) return highlightInfo;
}
}
}
return null;
}
public static HighlightInfo checkParametersOnRaw(PsiReferenceParameterList refParamList) {
if (refParamList.getTypeArguments().length == 0) return null;
JavaResolveResult resolveResult = null;
@@ -1363,42 +1209,6 @@ public class GenericsHighlightUtil {
return null;
}
public static HighlightInfo checkUncheckedOverriding (PsiMethod overrider, final List<HierarchicalMethodSignature> superMethodSignatures) {
if (!PsiUtil.isLanguageLevel5OrHigher(overrider)) return null;
final HighlightDisplayKey key = HighlightDisplayKey.find(UncheckedWarningLocalInspection.SHORT_NAME);
final InspectionProfile inspectionProfile =
InspectionProjectProfileManager.getInstance(overrider.getProject()).getInspectionProfile();
if (!inspectionProfile.isToolEnabled(key, overrider)) return null;
final LocalInspectionTool tool =
((LocalInspectionToolWrapper)inspectionProfile.getInspectionTool(UncheckedWarningLocalInspection.SHORT_NAME, overrider)).getTool();
if (InspectionManagerEx.inspectionResultSuppressed(overrider, tool)) return null;
final MethodSignature signature = overrider.getSignature(PsiSubstitutor.EMPTY);
for (MethodSignatureBackedByPsiMethod superSignature : superMethodSignatures) {
PsiMethod baseMethod = superSignature.getMethod();
PsiSubstitutor substitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(signature, superSignature);
if (substitutor == null) substitutor = superSignature.getSubstitutor();
if (PsiUtil.isRawSubstitutor(baseMethod, superSignature.getSubstitutor())) continue;
final PsiType baseReturnType = substitutor.substitute(baseMethod.getReturnType());
final PsiType overriderReturnType = overrider.getReturnType();
if (baseReturnType == null || overriderReturnType == null) return null;
if (isRawToGeneric(baseReturnType, overriderReturnType)) {
final String message = JavaErrorMessages.message("unchecked.overriding.incompatible.return.type",
HighlightUtil.formatType(overriderReturnType),
HighlightUtil.formatType(baseReturnType));
final PsiTypeElement returnTypeElement = overrider.getReturnTypeElement();
LOG.assertTrue(returnTypeElement != null);
final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(JavaHightlightInfoTypes.UNCHECKED_WARNING, returnTypeElement, message);
QuickFixAction.registerQuickFixAction(highlightInfo,
new EmptyIntentionAction(JavaErrorMessages.message("unchecked.overriding")),
key);
return highlightInfo;
}
}
return null;
}
public static HighlightInfo checkEnumMustNotBeLocal(final PsiClass aClass) {
if (!aClass.isEnum()) return null;
PsiElement parent = aClass.getParent();
@@ -317,13 +317,6 @@ public class HighlightMethodUtil {
if (element instanceof PsiMethod && resolveResult.isValidResult()) {
TextRange fixRange = getFixRange(methodCall);
highlightInfo = HighlightUtil.checkUnhandledExceptions(methodCall, fixRange);
if (highlightInfo == null) {
highlightInfo = GenericsHighlightUtil.checkUncheckedCall(resolveResult, methodCall);
}
if (highlightInfo == null) {
highlightInfo = GenericsHighlightUtil.checkGenericCallWithRawArguments(resolveResult, methodCall);
}
}
else {
PsiMethod resolvedMethod = null;
@@ -1271,11 +1264,7 @@ public class HighlightMethodUtil {
ChangeStringLiteralToCharInMethodCallFix.registerFixes(constructors, constructorCall, info);
}
else {
HighlightInfo highlightInfo = GenericsHighlightUtil.checkUncheckedCall(result, constructorCall);
if (highlightInfo != null) {
holder.add(highlightInfo);
return;
}
HighlightInfo highlightInfo;
if (constructorCall instanceof PsiNewExpression) {
highlightInfo = GenericsHighlightUtil.checkReferenceTypeArgumentList(constructor,
((PsiNewExpression)constructorCall).getTypeArgumentList(),
@@ -1284,13 +1273,7 @@ public class HighlightMethodUtil {
holder.add(highlightInfo);
return;
}
highlightInfo = GenericsHighlightUtil.checkGenericCallWithRawArguments(result, (PsiCallExpression)constructorCall);
if (highlightInfo != null) {
holder.add(highlightInfo);
}
//if (PsiUtil.isLanguageLevel7OrHigher(constructorCall)) {
// // todo[anna] check if not diamond - apply corresponding fix
//}
}
}
}
@@ -459,8 +459,7 @@ public class HighlightUtil {
if (rType == null || lType == null || TypeConversionUtil.isAssignable(lType, rType)) return null;
}
else if (TypeConversionUtil.areTypesAssignmentCompatible(lType, expression)) {
if (lType == null || rType == null) return null;
return GenericsHighlightUtil.checkRawToGenericAssignment(lType, rType, expression);
return null;
}
if (rType == null) {
rType = expression.getType();
@@ -1131,7 +1130,7 @@ public class HighlightUtil {
return null;
}
private static PsiType sameType(PsiExpression[] expressions) {
public static PsiType sameType(PsiExpression[] expressions) {
PsiType type = null;
for (PsiExpression expression : expressions) {
final PsiType currentType;
@@ -635,7 +635,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!myHolder.hasErrorResults()) myHolder.add(HighlightMethodUtil.checkMethodIncompatibleThrows(methodSignature, superMethodSignatures, true, method.getContainingClass()));
if (!method.hasModifierProperty(PsiModifier.STATIC)) {
if (!myHolder.hasErrorResults()) myHolder.add(HighlightMethodUtil.checkMethodWeakerPrivileges(methodSignature, superMethodSignatures, true));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkUncheckedOverriding(method, superMethodSignatures));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightMethodUtil.checkMethodOverridesFinal(methodSignature, superMethodSignatures));
}
}
@@ -686,7 +685,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkEnumInstantiation(expression));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkGenericArrayCreation(expression, expression.getType()));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkUncheckedGenericsArrayCreation(expression.getClassOrAnonymousClassReference(), expression.resolveConstructor()));
if (!myHolder.hasErrorResults()) registerConstructorCall(expression);
if (!myHolder.hasErrorResults()) visitExpression(expression);
@@ -837,7 +835,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!myHolder.hasErrorResults()) myHolder.add(HighlightMethodUtil.checkConstructorCallMustBeFirstStatement(expression));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkAccessStaticFieldFromEnumConstructor(expression, result));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkClassReferenceAfterQualifier(expression, resolved));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkUncheckedGenericsArrayCreation(expression, resolved));
}
@Override public void visitReferenceList(PsiReferenceList list) {
@@ -918,7 +915,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override public void visitTypeCastExpression(PsiTypeCastExpression typeCast) {
super.visitTypeCastExpression(typeCast);
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkInconvertibleTypeCast(typeCast));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkUncheckedTypeCast(typeCast));
}
@Override public void visitTypeParameterList(PsiTypeParameterList list) {
@@ -18,14 +18,19 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.refactoring.actions.TypeCookAction;
import org.jetbrains.annotations.NotNull;
public class GenerifyFileFix implements IntentionAction {
public class GenerifyFileFix implements IntentionAction, LocalQuickFix {
private final PsiFile myFile;
public GenerifyFileFix(PsiFile file) {
@@ -37,11 +42,28 @@ public class GenerifyFileFix implements IntentionAction {
return QuickFixBundle.message("generify.text", myFile.getName());
}
@NotNull
@Override
public String getName() {
return getText();
}
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("generify.family");
}
@Override
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
if (isAvailable(project, null, null)) {
new WriteCommandAction(project) {
protected void run(Result result) throws Throwable {
invoke(project, FileEditorManager.getInstance(project).getSelectedTextEditor(), descriptor.getPsiElement().getContainingFile());
}
}.execute();
}
}
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return myFile.isValid() && PsiManager.getInstance(project).isInProject(myFile);
}
@@ -18,6 +18,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.IntentionAndQuickFixAction;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
@@ -29,7 +30,7 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class VariableArrayTypeFix implements IntentionAction {
public class VariableArrayTypeFix extends IntentionAndQuickFixAction {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.VariableArrayTypeFix");
private final PsiVariable myVariable;
@@ -93,12 +94,15 @@ public class VariableArrayTypeFix implements IntentionAction {
}
@NotNull
public String getText() {
@Override
public String getName() {
return myTargetType.equals(myVariable.getType()) && myNewExpression != null ?
QuickFixBundle.message("change.new.operator.type.text", getNewText(), myTargetType.getCanonicalText(), "") :
QuickFixBundle.message("fix.variable.type.text", myVariable.getName(), myTargetType.getCanonicalText());
}
@NotNull
public String getFamilyName() {
return myTargetType.equals(myVariable.getType()) && myNewExpression != null ?
@@ -113,7 +117,8 @@ public class VariableArrayTypeFix implements IntentionAction {
&& myInitializer.isValid();
}
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
@Override
public void applyFix(Project project, PsiFile file, @Nullable Editor editor) {
if (!CodeInsightUtilBase.prepareFileForWrite(myVariable.getContainingFile())) return;
try {
final PsiElementFactory factory = JavaPsiFacade.getInstance(file.getProject()).getElementFactory();
@@ -18,6 +18,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.IntentionAndQuickFixAction;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
@@ -27,8 +28,9 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class VariableTypeFix implements IntentionAction {
public class VariableTypeFix extends IntentionAndQuickFixAction {
static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.VariableTypeFix");
private final PsiVariable myVariable;
@@ -39,8 +41,10 @@ public class VariableTypeFix implements IntentionAction {
myReturnType = toReturn != null ? GenericsUtil.getVariableTypeByExpressionType(toReturn) : null;
}
@NotNull
public String getText() {
@Override
public String getName() {
return QuickFixBundle.message("fix.variable.type.text",
getVariable().getName(),
getReturnType().getCanonicalText());
@@ -61,7 +65,8 @@ public class VariableTypeFix implements IntentionAction {
&& !TypeConversionUtil.isVoidType(getReturnType());
}
public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
@Override
public void applyFix(Project project, PsiFile file, @Nullable Editor editor) {
if (!CodeInsightUtilBase.prepareFileForWrite(getVariable().getContainingFile())) return;
try {
getVariable().normalizeDeclaration();
@@ -17,7 +17,9 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil;
import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
@@ -105,8 +107,8 @@ public class RedundantUncheckedSuppressWarningsInspection extends BaseJavaLocalI
}
private static void checkIfSafeToRemoveWarning(PsiElement suppressElement, PsiElement placeToCheckWarningsIn, ProblemsHolder holder) {
final HashSet<PsiReferenceExpression> warningsElements = new HashSet<PsiReferenceExpression>();
collectUncheckedWarnings(placeToCheckWarningsIn, true, warningsElements);
final HashSet<PsiElement> warningsElements = new HashSet<PsiElement>();
collectUncheckedWarnings(placeToCheckWarningsIn, warningsElements);
if (warningsElements.isEmpty()) {
final int uncheckedIdx = suppressElement.getText().indexOf(RemoveUncheckedWarningFix.UNCHECKED);
holder.registerProblem(suppressElement,
@@ -115,14 +117,19 @@ public class RedundantUncheckedSuppressWarningsInspection extends BaseJavaLocalI
}
}
public static void collectUncheckedWarnings(final PsiElement place, final boolean ignoreSuppressed, final Collection<PsiReferenceExpression> warningsElements) {
place.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
if (GenericsHighlightUtil.isUncheckedWarning(expression, expression.resolve(), ignoreSuppressed)) {
warningsElements.add(expression);
public static void collectUncheckedWarnings(final PsiElement place, final Collection<PsiElement> warningsElements) {
final UncheckedWarningLocalInspection.UncheckedWarningsVisitor visitor =
new UncheckedWarningLocalInspection.UncheckedWarningsVisitor(false) {
@Override
protected void registerProblem(String message, PsiElement psiElement, LocalQuickFix... quickFix) {
warningsElements.add(psiElement);
}
};
place.accept(new JavaRecursiveElementVisitor(){
@Override
public void visitElement(PsiElement element) {
super.visitElement(element);
element.accept(visitor);
}
});
}
@@ -16,20 +16,38 @@
package com.intellij.codeInspection.uncheckedWarnings;
import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.GenerifyFileFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.daemon.impl.quickfix.VariableArrayTypeFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.quickfix.ChangeVariableTypeQuickFixProvider;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.UnfairLocalInspectionTool;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.*;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* User: anna
* Date: 17-Feb-2006
*/
public class UncheckedWarningLocalInspection extends BaseJavaLocalInspectionTool implements UnfairLocalInspectionTool {
public class UncheckedWarningLocalInspection extends BaseJavaLocalInspectionTool {
@NonNls public static final String SHORT_NAME = "UNCHECKED_WARNING";
public static final String DISPLAY_NAME = InspectionsBundle.message("unchecked.warning");
@NonNls public static final String ID = "unchecked";
private static final Logger LOG = Logger.getInstance("#" + UncheckedWarningLocalInspection.class);
@NotNull
public String getGroupDisplayName() {
@@ -56,4 +74,303 @@ public class UncheckedWarningLocalInspection extends BaseJavaLocalInspectionTool
public boolean isEnabledByDefault() {
return true;
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
return new UncheckedWarningsVisitor(isOnTheFly){
@Override
protected void registerProblem(String message, PsiElement psiElement, LocalQuickFix... quickFix) {
holder.registerProblem(psiElement, message, quickFix);
}
};
}
public static abstract class UncheckedWarningsVisitor extends JavaElementVisitor {
private final boolean myOnTheFly;
public UncheckedWarningsVisitor(boolean onTheFly) {
myOnTheFly = onTheFly;
}
protected abstract void registerProblem(String message, PsiElement psiElement, LocalQuickFix... quickFix);
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return;
if (GenericsHighlightUtil.isUncheckedWarning(expression, expression.resolve())) {
registerProblem("Unchecked generics array creation for varargs parameter", expression, null);
}
}
@Override
public void visitNewExpression(PsiNewExpression expression) {
super.visitNewExpression(expression);
if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return;
final PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
if (GenericsHighlightUtil.isUncheckedWarning(classReference, expression.resolveConstructor())) {
registerProblem("Unchecked generics array creation for varargs parameter", classReference, null);
}
}
@Override
public void visitTypeCastExpression(PsiTypeCastExpression expression) {
super.visitTypeCastExpression(expression);
if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return;
final PsiTypeElement typeElement = expression.getCastType();
if (typeElement == null) return;
final PsiType castType = typeElement.getType();
final PsiExpression operand = expression.getOperand();
if (operand == null) return;
final PsiType exprType = operand.getType();
if (exprType == null) return;
if (!TypeConversionUtil.areTypesConvertible(exprType, castType)) return;
if (GenericsHighlightUtil.isUncheckedCast(castType, exprType)) {
final String description =
JavaErrorMessages.message("generics.unchecked.cast", HighlightUtil.formatType(exprType), HighlightUtil.formatType(castType));
registerProblem(description, expression, myOnTheFly ? new GenerifyFileFix(operand.getContainingFile()) : null);
}
}
@Override
public void visitCallExpression(PsiCallExpression callExpression) {
super.visitCallExpression(callExpression);
if (!PsiUtil.isLanguageLevel5OrHigher(callExpression)) return;
final JavaResolveResult result = callExpression.resolveMethodGenerics();
final String description = getUncheckedCallDescription(result);
if (description != null) {
registerProblem(description, callExpression instanceof PsiMethodCallExpression
? ((PsiMethodCallExpression)callExpression).getMethodExpression()
: callExpression, myOnTheFly ? new GenerifyFileFix(callExpression.getContainingFile()) : null);
}
else {
final PsiSubstitutor substitutor = result.getSubstitutor();
final PsiExpressionList argumentList = callExpression.getArgumentList();
if (argumentList != null) {
final PsiMethod method = (PsiMethod)result.getElement();
if (method != null) {
final PsiExpression[] expressions = argumentList.getExpressions();
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != 0) {
for (int i = 0; i < expressions.length; i++) {
PsiParameter parameter = parameters[Math.min(i, parameters.length - 1)];
final PsiExpression expression = expressions[i];
final PsiType parameterType = substitutor.substitute(parameter.getType());
final PsiType expressionType = substitutor.substitute(expression.getType());
if (expressionType != null) {
checkRawToGenericsAssignment(expression, parameterType, expressionType, true, myOnTheFly ? new GenerifyFileFix(expression.getContainingFile()) : null);
}
}
}
}
}
}
}
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
if (!PsiUtil.isLanguageLevel5OrHigher(variable)) return;
PsiExpression initializer = variable.getInitializer();
if (initializer == null || initializer instanceof PsiArrayInitializerExpression) return;
final PsiType initializerType = initializer.getType();
checkRawToGenericsAssignment(initializer, variable.getType(), initializerType, true, myOnTheFly ? getChangeVariableTypeFixes(variable, initializerType) : null);
}
@Override
public void visitForeachStatement(PsiForeachStatement statement) {
super.visitForeachStatement(statement);
if (!PsiUtil.isLanguageLevel5OrHigher(statement)) return;
final PsiParameter parameter = statement.getIterationParameter();
final PsiType parameterType = parameter.getType();
final PsiType itemType = GenericsHighlightUtil.getCollectionItemType(statement.getIteratedValue());
if (!PsiUtil.isLanguageLevel5OrHigher(statement)) return;
checkRawToGenericsAssignment(parameter, parameterType, itemType, true, myOnTheFly ? getChangeVariableTypeFixes(parameter, itemType) : null);
}
@Override
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
super.visitAssignmentExpression(expression);
if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return;
if (!"=".equals(expression.getOperationSign().getText())) return;
PsiExpression lExpr = expression.getLExpression();
PsiExpression rExpr = expression.getRExpression();
if (rExpr == null) return;
PsiType lType = lExpr.getType();
PsiType rType = rExpr.getType();
if (rType == null) return;
PsiVariable leftVar = null;
if (lExpr instanceof PsiReferenceExpression) {
PsiElement element = ((PsiReferenceExpression)lExpr).resolve();
if (element instanceof PsiVariable) {
leftVar = (PsiVariable)element;
}
}
checkRawToGenericsAssignment(rExpr, lType, rType, true, myOnTheFly && leftVar != null ? getChangeVariableTypeFixes(leftVar, rType) : null);
}
@Override
public void visitArrayInitializerExpression(PsiArrayInitializerExpression arrayInitializer) {
super.visitArrayInitializerExpression(arrayInitializer);
if (!PsiUtil.isLanguageLevel5OrHigher(arrayInitializer)) return;
final PsiType type = arrayInitializer.getType();
if (!(type instanceof PsiArrayType)) return;
final PsiType componentType = ((PsiArrayType)type).getComponentType();
boolean arrayTypeFixChecked = false;
VariableArrayTypeFix fix = null;
final PsiExpression[] initializers = arrayInitializer.getInitializers();
for (PsiExpression expression : initializers) {
final PsiType itemType = expression.getType();
if (itemType == null) continue;
if (!TypeConversionUtil.isAssignable(componentType, itemType)) continue;
if (GenericsHighlightUtil.isRawToGeneric(componentType, itemType)) {
String description = JavaErrorMessages.message("generics.unchecked.assignment",
HighlightUtil.formatType(itemType),
HighlightUtil.formatType(componentType));
if (!arrayTypeFixChecked) {
final PsiType checkResult = HighlightUtil.sameType(initializers);
fix = checkResult != null ? new VariableArrayTypeFix(arrayInitializer, checkResult) : null;
arrayTypeFixChecked = true;
}
if (fix != null) {
registerProblem(description, expression, (LocalQuickFix)fix);
}
}
}
}
private void checkRawToGenericsAssignment(PsiElement parameter,
PsiType parameterType,
PsiType itemType,
boolean checkAssignability,
final LocalQuickFix... quickFix) {
if (parameterType == null || itemType == null) return;
if (checkAssignability && !TypeConversionUtil.isAssignable(parameterType, itemType)) return;
if (GenericsHighlightUtil.isRawToGeneric(parameterType, itemType)) {
String description = JavaErrorMessages.message("generics.unchecked.assignment",
HighlightUtil.formatType(itemType),
HighlightUtil.formatType(parameterType));
registerProblem(description, parameter, quickFix);
}
}
@Override
public void visitMethod(PsiMethod method) {
super.visitMethod(method);
if (!PsiUtil.isLanguageLevel5OrHigher(method)) return;
if (!method.isConstructor()) {
List<HierarchicalMethodSignature> superMethodSignatures = method.getHierarchicalMethodSignature().getSuperSignatures();
if (!superMethodSignatures.isEmpty() && !method.hasModifierProperty(PsiModifier.STATIC)) {
final MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY);
for (MethodSignatureBackedByPsiMethod superSignature : superMethodSignatures) {
PsiMethod baseMethod = superSignature.getMethod();
PsiSubstitutor substitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(signature, superSignature);
if (substitutor == null) substitutor = superSignature.getSubstitutor();
if (PsiUtil.isRawSubstitutor(baseMethod, superSignature.getSubstitutor())) continue;
final PsiType baseReturnType = substitutor.substitute(baseMethod.getReturnType());
final PsiType overriderReturnType = method.getReturnType();
if (baseReturnType == null || overriderReturnType == null) return;
if (GenericsHighlightUtil.isRawToGeneric(baseReturnType, overriderReturnType)) {
final String message = JavaErrorMessages.message("unchecked.overriding.incompatible.return.type",
HighlightUtil.formatType(overriderReturnType),
HighlightUtil.formatType(baseReturnType));
final PsiTypeElement returnTypeElement = method.getReturnTypeElement();
LOG.assertTrue(returnTypeElement != null);
registerProblem(message, returnTypeElement);
}
}
}
}
}
@Override
public void visitReturnStatement(PsiReturnStatement statement) {
super.visitReturnStatement(statement);
if (!PsiUtil.isLanguageLevel5OrHigher(statement)) return;
final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
if (method != null) {
final PsiType returnType = method.getReturnType();
if (returnType != null && returnType != PsiType.VOID) {
final PsiExpression returnValue = statement.getReturnValue();
if (returnValue != null) {
final PsiType valueType = returnValue.getType();
if (valueType != null) {
checkRawToGenericsAssignment(returnValue, returnType, valueType,
false,
(LocalQuickFix)QuickFixFactory.getInstance().createMethodReturnFix(method, valueType, true));
}
}
}
}
}
@Nullable
public static String getUncheckedCallDescription(JavaResolveResult resolveResult) {
final PsiMethod method = (PsiMethod)resolveResult.getElement();
if (method == null) return null;
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
final PsiParameter[] parameters = method.getParameterList().getParameters();
for (final PsiParameter parameter : parameters) {
final PsiType parameterType = parameter.getType();
if (parameterType.accept(new PsiTypeVisitor<Boolean>() {
public Boolean visitPrimitiveType(PsiPrimitiveType primitiveType) {
return Boolean.FALSE;
}
public Boolean visitArrayType(PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
public Boolean visitClassType(PsiClassType classType) {
PsiClass psiClass = classType.resolve();
if (psiClass instanceof PsiTypeParameter) {
return substitutor.substitute((PsiTypeParameter)psiClass) == null ? Boolean.TRUE : Boolean.FALSE;
}
PsiType[] parameters = classType.getParameters();
for (PsiType parameter : parameters) {
if (parameter.accept(this).booleanValue()) return Boolean.TRUE;
}
return Boolean.FALSE;
}
public Boolean visitWildcardType(PsiWildcardType wildcardType) {
PsiType bound = wildcardType.getBound();
if (bound != null) return bound.accept(this);
return Boolean.FALSE;
}
public Boolean visitEllipsisType(PsiEllipsisType ellipsisType) {
return ellipsisType.getComponentType().accept(this);
}
}).booleanValue()) {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
PsiType type = elementFactory.createType(method.getContainingClass(), substitutor);
return JavaErrorMessages.message("generics.unchecked.call.to.member.of.raw.type",
HighlightUtil.formatMethod(method),
HighlightUtil.formatType(type));
}
}
return null;
}
}
public static LocalQuickFix[] getChangeVariableTypeFixes(PsiVariable parameter, PsiType itemType) {
final List<LocalQuickFix> result = new ArrayList<LocalQuickFix>();
for (ChangeVariableTypeQuickFixProvider fixProvider : Extensions.getExtensions(ChangeVariableTypeQuickFixProvider.EP_NAME)) {
for (IntentionAction action : fixProvider.getFixes(parameter, itemType)) {
if (action instanceof LocalQuickFix) {
result.add((LocalQuickFix)action);
}
}
}
return result.toArray(new LocalQuickFix[result.size()]);
}
}
@@ -86,12 +86,12 @@ class SemKey<T extends String> {
}
public static <T> T[] append(final T[] src, final T element) {
return append(src, element, (Class<T>)src.getClass().getComponentType());
return append(src, element, <warning descr="Unchecked cast: 'java.lang.Class<capture<?>>' to 'java.lang.Class<T>'">(Class<T>)src.getClass().getComponentType()</warning>);
}
public static <T> T[] append(T[] src, final T element, Class<T> componentType) {
int length = src.length;
T[] result = (T[])java.lang.reflect.Array.newInstance(componentType, length + 1);
T[] result = <warning descr="Unchecked cast: 'java.lang.Object' to 'T[]'">(T[])java.lang.reflect.Array.newInstance(componentType, length + 1)</warning>;
System.arraycopy(src, 0, result, 0, length);
result[length] = element;
return result;
@@ -118,4 +118,4 @@ class OCM<T> {
OCM(T s, Condition<T>... c) {
this(s, false, c);
}
}
}
@@ -8,7 +8,7 @@ class Test {
}
void foo() {
<error descr="Incompatible types. Found: 'java.util.List<java.lang.Class<? extends java.io.Serializable & java.lang.Comparable<?>>>', required: 'java.util.List<java.lang.Class<? extends java.io.Serializable>>'">List<Class<? extends Serializable>> l = this.asList(String.class, Integer.class);</error>
<error descr="Incompatible types. Found: 'java.util.List<java.lang.Class<? extends java.io.Serializable & java.lang.Comparable<?>>>', required: 'java.util.List<java.lang.Class<? extends java.io.Serializable>>'">List<Class<? extends Serializable>> l = <warning descr="Unchecked generics array creation for varargs parameter">this.asList</warning>(String.class, Integer.class);</error>
l.size();
List<? extends Object> objects = this.asList(new String(), new Integer(0));
objects.size();
@@ -131,7 +131,7 @@ class IDEADEV25515 {
public static final
<error descr="Incompatible types. Found: 'java.util.List<java.lang.Class<? extends java.io.Serializable & java.lang.Comparable<?>>>', required: 'java.util.List<java.lang.Class<? extends java.io.Serializable>>'">List<Class<? extends Serializable>> SIMPLE_TYPES =
asList(String.class, Integer.class ,Long.class, Double.class, /*Date.class,*/
<warning descr="Unchecked generics array creation for varargs parameter">asList</warning>(String.class, Integer.class ,Long.class, Double.class, /*Date.class,*/
Boolean.class, Boolean.TYPE /*,String[].class */ /*,BigDecimal.class*/);</error>
@@ -162,4 +162,4 @@ public class MaximalType {
}
class M extends MaximalType implements L{}
class M2 extends MaximalType implements L{}
/////////////
/////////////
@@ -9,7 +9,7 @@ public class Test {
void foo() {
//noinspection unc<caret>hecked
foo(new ArrayList<String>()).addAll(Arrays.asList(new ArrayList<String>);
foo(new ArrayList<String>()).addAll(Arrays.asList(new ArrayList<String>()));
}
}
@@ -6,6 +6,7 @@ import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.reference.EntryPoint;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.extensions.ExtensionPoint;
@@ -30,7 +31,7 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase {
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new UnusedSymbolLocalInspection()};
return new LocalInspectionTool[]{new UnusedSymbolLocalInspection(), new UncheckedWarningLocalInspection()};
}
public void testDuplicateAnnotations() throws Exception {