mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+32
-23
@@ -21,9 +21,9 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -40,45 +40,54 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SimplifyBooleanExpressionFix implements IntentionAction {
|
||||
public class SimplifyBooleanExpressionFix extends LocalQuickFixOnPsiElement {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpression");
|
||||
public static final String FAMILY_NAME = QuickFixBundle.message("simplify.boolean.expression.family");
|
||||
|
||||
private final PsiExpression mySubExpression;
|
||||
private final Boolean mySubExpressionValue;
|
||||
|
||||
// subExpressionValue == Boolean.TRUE or Boolean.FALSE if subExpression evaluates to boolean constant and needs to be replaced
|
||||
// otherwise subExpressionValue= null and we starting to simplify expression without any further knowledge
|
||||
public SimplifyBooleanExpressionFix(PsiExpression subExpression, Boolean subExpressionValue) {
|
||||
mySubExpression = subExpression;
|
||||
public SimplifyBooleanExpressionFix(@NotNull PsiExpression subExpression, Boolean subExpressionValue) {
|
||||
super(subExpression);
|
||||
mySubExpressionValue = subExpressionValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getText() {
|
||||
return QuickFixBundle.message("simplify.boolean.expression.text", mySubExpression.getText(), mySubExpressionValue);
|
||||
PsiExpression expression = getSubExpression();
|
||||
return QuickFixBundle.message("simplify.boolean.expression.text", expression.getText(), mySubExpressionValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return QuickFixBundle.message("simplify.boolean.expression.family");
|
||||
return FAMILY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
return mySubExpression.isValid()
|
||||
&& mySubExpression.getManager().isInProject(mySubExpression)
|
||||
&& !PsiUtil.isAccessedForWriting(mySubExpression)
|
||||
;
|
||||
public boolean isAvailable() {
|
||||
PsiExpression expression = getSubExpression();
|
||||
return super.isAvailable()
|
||||
&& expression != null
|
||||
&& expression.isValid()
|
||||
&& expression.getManager().isInProject(expression)
|
||||
&& !PsiUtil.isAccessedForWriting(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
if (!isAvailable(project, editor, file)) return;
|
||||
LOG.assertTrue(mySubExpression.isValid());
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(mySubExpression)) return;
|
||||
simplifyExpression(project, mySubExpression, mySubExpressionValue);
|
||||
public void invoke(@NotNull final Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
|
||||
if (!isAvailable()) return;
|
||||
final PsiExpression expression = getSubExpression();
|
||||
LOG.assertTrue(expression.isValid());
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(expression)) return;
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
simplifyExpression(project, expression, mySubExpressionValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void simplifyExpression(Project project, final PsiExpression subExpression, final Boolean subExpressionValue) {
|
||||
@@ -226,6 +235,11 @@ public class SimplifyBooleanExpressionFix implements IntentionAction {
|
||||
return canBeSimplified.get().booleanValue();
|
||||
}
|
||||
|
||||
private PsiExpression getSubExpression() {
|
||||
PsiElement element = getStartElement();
|
||||
return element instanceof PsiExpression ? (PsiExpression)element : null;
|
||||
}
|
||||
|
||||
private static class ExpressionVisitor extends JavaElementVisitor {
|
||||
private PsiExpression resultExpression;
|
||||
private final PsiExpression trueExpression;
|
||||
@@ -420,9 +434,4 @@ public class SimplifyBooleanExpressionFix implements IntentionAction {
|
||||
String text = operand.getText();
|
||||
return PsiKeyword.TRUE.equals(text) ? Boolean.TRUE : PsiKeyword.FALSE.equals(text) ? Boolean.FALSE : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.guess.impl;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtil;
|
||||
import com.intellij.codeInsight.JavaPsiEquivalenceUtil;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryStateImpl;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaInstanceofValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -41,7 +42,7 @@ public class ExpressionTypeMemoryState extends DfaMemoryStateImpl {
|
||||
|
||||
@Override
|
||||
public boolean equals(PsiExpression o1, PsiExpression o2) {
|
||||
if (CodeInsightUtil.areExpressionsEquivalent(o1, o2)) {
|
||||
if (JavaPsiEquivalenceUtil.areExpressionsEquivalent(o1, o2)) {
|
||||
if (computeHashCode(o1) != computeHashCode(o2)) {
|
||||
LOG.error("different hashCodes: " + o1 + "; " + o2 + "; " + computeHashCode(o1) + "!=" + computeHashCode(o2));
|
||||
}
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -22,6 +22,7 @@ import com.intellij.codeInspection.dataFlow.instructions.PushInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.TypeCastInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.InstanceofInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaInstanceofValue;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
@@ -140,7 +141,7 @@ public class GuessManagerImpl extends GuessManager {
|
||||
|
||||
@Nullable
|
||||
private static Map<PsiExpression, PsiType> buildDataflowTypeMap(PsiExpression forPlace) {
|
||||
PsiElement scope = DfaUtil.getTopmostBlockInSameClass(forPlace);
|
||||
PsiElement scope = DfaPsiUtil.getTopmostBlockInSameClass(forPlace);
|
||||
if (scope == null) {
|
||||
PsiFile file = forPlace.getContainingFile();
|
||||
if (!(file instanceof PsiCodeFragment)) {
|
||||
+4
-4
@@ -23,14 +23,14 @@
|
||||
package com.intellij.codeInsight.intention.impl;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationFix;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class AddNullableNotNullAnnotationFix extends AddAnnotationFix {
|
||||
public class AddNullableNotNullAnnotationFix extends AddAnnotationPsiFix {
|
||||
public AddNullableNotNullAnnotationFix(@NotNull String fqn, @NotNull PsiModifierListOwner owner, @NotNull String... annotationToRemove) {
|
||||
super(fqn, owner, annotationToRemove);
|
||||
super(fqn, owner, PsiNameValuePair.EMPTY_ARRAY, annotationToRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,7 +38,7 @@ public class AddNullableNotNullAnnotationFix extends AddAnnotationFix {
|
||||
@NotNull PsiFile file,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
if (!super.isAvailable(project, file, startElement, endElement)) {
|
||||
if (!super.isAvailable(project, file, startElement, endElement)) {
|
||||
return false;
|
||||
}
|
||||
PsiModifierListOwner owner = getContainer(startElement);
|
||||
+1
-1
@@ -54,7 +54,7 @@ public class AddAssertStatementFix implements LocalQuickFix {
|
||||
PsiElement anchorElement = PsiTreeUtil.getParentOfType(element, PsiStatement.class);
|
||||
LOG.assertTrue(anchorElement != null);
|
||||
PsiElement prev = PsiTreeUtil.skipSiblingsBackward(anchorElement, PsiWhiteSpace.class);
|
||||
if (prev instanceof PsiComment && SuppressManager.getInstance().getSuppressedInspectionIdsIn(prev) != null) {
|
||||
if (prev instanceof PsiComment && JavaSuppressionUtil.getSuppressedInspectionIdsIn(prev) != null) {
|
||||
anchorElement = prev;
|
||||
}
|
||||
|
||||
+9
-16
@@ -17,20 +17,18 @@ package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationFix;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
||||
import com.intellij.openapi.command.undo.UndoUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.psi.PsiNameValuePair;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -45,7 +43,7 @@ public class AnnotateMethodFix implements LocalQuickFix {
|
||||
protected final String myAnnotation;
|
||||
private final String[] myAnnotationsToRemove;
|
||||
|
||||
public AnnotateMethodFix(final String fqn, String... annotationsToRemove) {
|
||||
public AnnotateMethodFix(@NotNull String fqn, @NotNull String... annotationsToRemove) {
|
||||
myAnnotation = fqn;
|
||||
myAnnotationsToRemove = annotationsToRemove;
|
||||
}
|
||||
@@ -68,7 +66,7 @@ public class AnnotateMethodFix implements LocalQuickFix {
|
||||
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
|
||||
PsiMethod superMethod = superMethodSignature.getMethod();
|
||||
if (!AnnotationUtil.isAnnotated(superMethod, myAnnotation, false, false) && superMethod.getManager().isInProject(superMethod)) {
|
||||
int ret = annotateBaseMethod(method, superMethod, project);
|
||||
int ret = shouldAnnotateBaseMethod(method, superMethod, project);
|
||||
if (ret != 0 && ret != 1) return;
|
||||
if (ret == 0) {
|
||||
toAnnotate.add(superMethod);
|
||||
@@ -91,15 +89,9 @@ public class AnnotateMethodFix implements LocalQuickFix {
|
||||
UndoUtil.markPsiFileForUndo(method.getContainingFile());
|
||||
}
|
||||
|
||||
public int annotateBaseMethod(final PsiMethod method, final PsiMethod superMethod, final Project project) {
|
||||
String implement = !method.hasModifierProperty(PsiModifier.ABSTRACT) && superMethod.hasModifierProperty(PsiModifier.ABSTRACT)
|
||||
? InspectionsBundle.message("inspection.annotate.quickfix.implements")
|
||||
: InspectionsBundle.message("inspection.annotate.quickfix.overrides");
|
||||
String message = InspectionsBundle.message("inspection.annotate.quickfix.overridden.method.messages",
|
||||
UsageViewUtil.getDescriptiveName(method), implement,
|
||||
UsageViewUtil.getDescriptiveName(superMethod));
|
||||
String title = InspectionsBundle.message("inspection.annotate.quickfix.overridden.method.warning");
|
||||
return Messages.showYesNoCancelDialog(project, message, title, Messages.getQuestionIcon());
|
||||
// 0-annotate, 1-do not annotate, 2- cancel
|
||||
public int shouldAnnotateBaseMethod(final PsiMethod method, final PsiMethod superMethod, final Project project) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected boolean annotateOverriddenMethods() {
|
||||
@@ -114,7 +106,8 @@ public class AnnotateMethodFix implements LocalQuickFix {
|
||||
|
||||
private void annotateMethod(@NotNull PsiMethod method) {
|
||||
try {
|
||||
new AddAnnotationFix(myAnnotation, method, myAnnotationsToRemove).invoke(method.getProject(), null, method.getContainingFile());
|
||||
AddAnnotationPsiFix fix = new AddAnnotationPsiFix(myAnnotation, method, PsiNameValuePair.EMPTY_ARRAY, myAnnotationsToRemove);
|
||||
fix.invoke(method.getProject(), method.getContainingFile(), method, method);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
+1
-20
@@ -16,14 +16,10 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -66,22 +62,7 @@ public class ReplaceWithTernaryOperatorFix implements LocalQuickFix {
|
||||
|
||||
final PsiFile file = expression.getContainingFile();
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
|
||||
final PsiConditionalExpression conditionalExpression = replaceWthConditionalExpression(project, myText + "!=null", expression, suggestDefaultValue(expression));
|
||||
|
||||
final PsiExpression elseExpression = conditionalExpression.getElseExpression();
|
||||
if (elseExpression != null) {
|
||||
selectInEditor(elseExpression);
|
||||
}
|
||||
}
|
||||
|
||||
private static void selectInEditor(@NotNull PsiElement element) {
|
||||
final Editor editor = PsiUtilBase.findEditor(element);
|
||||
if (editor == null) return;
|
||||
|
||||
final TextRange expressionRange = element.getTextRange();
|
||||
editor.getCaretModel().moveToOffset(expressionRange.getStartOffset());
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
editor.getSelectionModel().setSelection(expressionRange.getStartOffset(), expressionRange.getEndOffset());
|
||||
replaceWthConditionalExpression(project, myText + "!=null", expression, suggestDefaultValue(expression));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.codeInspection.accessStaticViaInstance;
|
||||
|
||||
import com.intellij.codeInsight.daemon.JavaErrorMessages;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil;
|
||||
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AccessStaticViaInstanceBase extends BaseJavaBatchLocalInspectionTool {
|
||||
@NonNls public static final String ACCESS_STATIC_VIA_INSTANCE = "AccessStaticViaInstance";
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("access.static.via.instance");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
@NonNls
|
||||
public String getShortName() {
|
||||
return ACCESS_STATIC_VIA_INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlternativeID() {
|
||||
return "static-access";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
checkAccessStaticMemberViaInstanceReference(expression, holder, isOnTheFly);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void checkAccessStaticMemberViaInstanceReference(PsiReferenceExpression expr, ProblemsHolder holder, boolean onTheFly) {
|
||||
JavaResolveResult result = expr.advancedResolve(false);
|
||||
PsiElement resolved = result.getElement();
|
||||
|
||||
if (!(resolved instanceof PsiMember)) return;
|
||||
PsiExpression qualifierExpression = expr.getQualifierExpression();
|
||||
if (qualifierExpression == null) return;
|
||||
|
||||
if (qualifierExpression instanceof PsiReferenceExpression) {
|
||||
final PsiElement qualifierResolved = ((PsiReferenceExpression)qualifierExpression).resolve();
|
||||
if (qualifierResolved instanceof PsiClass || qualifierResolved instanceof PsiPackage) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC)) return;
|
||||
|
||||
String description = JavaErrorMessages.message("static.member.accessed.via.instance.reference",
|
||||
JavaHighlightUtil.formatType(qualifierExpression.getType()),
|
||||
HighlightMessageUtil.getSymbolName(resolved, result.getSubstitutor()));
|
||||
if (!onTheFly) {
|
||||
if (RemoveUnusedVariableUtil.checkSideEffects(qualifierExpression, null, new ArrayList<PsiElement>())) {
|
||||
holder.registerProblem(expr, description);
|
||||
return;
|
||||
}
|
||||
}
|
||||
holder.registerProblem(expr, description, createAccessStaticViaInstanceFix(expr, onTheFly, result));
|
||||
}
|
||||
|
||||
protected LocalQuickFix createAccessStaticViaInstanceFix(PsiReferenceExpression expr,
|
||||
boolean onTheFly,
|
||||
JavaResolveResult result) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -19,7 +19,7 @@
|
||||
* User: max
|
||||
* Date: Jan 11, 2002
|
||||
* Time: 3:05:34 PM
|
||||
* To change template for new class use
|
||||
* To change template for new class use
|
||||
* Code Style | Class Templates options (Tools | IDE Options).
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
@@ -33,9 +33,10 @@ import com.intellij.psi.PsiVariable;
|
||||
import gnu.trove.TObjectIntHashMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ControlFlow {
|
||||
private final ArrayList<Instruction> myInstructions = new ArrayList<Instruction>();
|
||||
private final List<Instruction> myInstructions = new ArrayList<Instruction>();
|
||||
private final TObjectIntHashMap<PsiElement> myElementToStartOffsetMap = new TObjectIntHashMap<PsiElement>();
|
||||
private final TObjectIntHashMap<PsiElement> myElementToEndOffsetMap = new TObjectIntHashMap<PsiElement>();
|
||||
private DfaVariableValue[] myFields;
|
||||
@@ -92,7 +93,7 @@ public class ControlFlow {
|
||||
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
final ArrayList<Instruction> instructions = myInstructions;
|
||||
final List<Instruction> instructions = myInstructions;
|
||||
|
||||
for (int i = 0; i < instructions.size(); i++) {
|
||||
Instruction instruction = instructions.get(i);
|
||||
+2
-2
@@ -1573,7 +1573,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
if (dfaValue == null) {
|
||||
PsiType type = expression.getType();
|
||||
return myFactory.createTypeValueWithNullability(type, DfaUtil.getElementNullability(type, field));
|
||||
return myFactory.createTypeValueWithNullability(type, DfaPsiUtil.getElementNullability(type, field));
|
||||
}
|
||||
return dfaValue;
|
||||
}
|
||||
@@ -1610,7 +1610,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (DfaUtil.isFinalField(var) || DfaUtil.isPlainMutableField(var)) {
|
||||
if (DfaPsiUtil.isFinalField(var) || DfaPsiUtil.isPlainMutableField(var)) {
|
||||
DfaVariableValue qualifierValue = createChainedVariableValue(qualifier);
|
||||
if (qualifierValue != null) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), false, qualifierValue, isCall || qualifierValue.isViaMethods());
|
||||
+590
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: max
|
||||
* Date: Dec 24, 2001
|
||||
* Time: 2:46:32 PM
|
||||
* To change template for new class use
|
||||
* Code Style | Class Templates options (Tools | IDE Options).
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpressionFix;
|
||||
import com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DataFlowInspection");
|
||||
@NonNls private static final String SHORT_NAME = "ConstantConditions";
|
||||
public boolean SUGGEST_NULLABLE_ANNOTATIONS = false;
|
||||
public boolean DONT_REPORT_TRUE_ASSERT_STATEMENTS = false;
|
||||
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
throw new RuntimeException("no UI in headless mode");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitField(PsiField field) {
|
||||
analyzeCodeBlock(field, holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethod(PsiMethod method) {
|
||||
analyzeCodeBlock(method.getBody(), holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassInitializer(PsiClassInitializer initializer) {
|
||||
analyzeCodeBlock(initializer.getBody(), holder);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void analyzeCodeBlock(@Nullable final PsiElement scope, ProblemsHolder holder) {
|
||||
if (scope == null) return;
|
||||
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(SUGGEST_NULLABLE_ANNOTATIONS);
|
||||
final StandardInstructionVisitor visitor = new DataFlowInstructionVisitor(dfaRunner);
|
||||
final RunnerResult rc = dfaRunner.analyzeMethod(scope, visitor);
|
||||
if (rc == RunnerResult.OK) {
|
||||
if (dfaRunner.problemsDetected(visitor)) {
|
||||
createDescription(dfaRunner, holder, visitor);
|
||||
}
|
||||
}
|
||||
else if (rc == RunnerResult.TOO_COMPLEX) {
|
||||
if (scope.getParent() instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod)scope.getParent();
|
||||
final PsiIdentifier name = method.getNameIdentifier();
|
||||
if (name != null) { // Might be null for synthetic methods like JSP page.
|
||||
holder.registerProblem(name, InspectionsBundle.message("dataflow.too.complex"), ProblemHighlightType.WEAK_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private LocalQuickFix[] createNPEFixes(PsiExpression qualifier, PsiExpression expression) {
|
||||
if (qualifier == null || expression == null) return null;
|
||||
if (qualifier instanceof PsiMethodCallExpression) return null;
|
||||
if (qualifier instanceof PsiLiteralExpression && ((PsiLiteralExpression)qualifier).getValue() == null) return null;
|
||||
|
||||
try {
|
||||
final List<LocalQuickFix> fixes = new SmartList<LocalQuickFix>();
|
||||
|
||||
if (PsiUtil.getLanguageLevel(qualifier).isAtLeast(LanguageLevel.JDK_1_4)) {
|
||||
final Project project = qualifier.getProject();
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
final PsiBinaryExpression binary = (PsiBinaryExpression)elementFactory.createExpressionFromText("a != null", null);
|
||||
binary.getLOperand().replace(qualifier);
|
||||
fixes.add(new AddAssertStatementFix(binary));
|
||||
}
|
||||
|
||||
addSurroundWithIfFix(qualifier, fixes);
|
||||
|
||||
if (ReplaceWithTernaryOperatorFix.isAvailable(qualifier, expression)) {
|
||||
fixes.add(new ReplaceWithTernaryOperatorFix(qualifier));
|
||||
}
|
||||
return fixes.toArray(new LocalQuickFix[fixes.size()]);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void addSurroundWithIfFix(PsiExpression qualifier, List<LocalQuickFix> fixes) {
|
||||
}
|
||||
|
||||
private void createDescription(StandardDataFlowRunner runner, ProblemsHolder holder, StandardInstructionVisitor visitor) {
|
||||
Pair<Set<Instruction>, Set<Instruction>> constConditions = runner.getConstConditionalExpressions();
|
||||
Set<Instruction> trueSet = constConditions.getFirst();
|
||||
Set<Instruction> falseSet = constConditions.getSecond();
|
||||
|
||||
ArrayList<Instruction> allProblems = new ArrayList<Instruction>();
|
||||
allProblems.addAll(trueSet);
|
||||
allProblems.addAll(falseSet);
|
||||
allProblems.addAll(runner.getNPEInstructions());
|
||||
allProblems.addAll(runner.getCCEInstructions());
|
||||
allProblems.addAll(StandardDataFlowRunner.getRedundantInstanceofs(runner, visitor));
|
||||
|
||||
Collections.sort(allProblems, new Comparator<Instruction>() {
|
||||
@Override
|
||||
public int compare(Instruction i1, Instruction i2) {
|
||||
return i1.getIndex() - i2.getIndex();
|
||||
}
|
||||
});
|
||||
|
||||
HashSet<PsiElement> reportedAnchors = new HashSet<PsiElement>();
|
||||
|
||||
for (Instruction instruction : allProblems) {
|
||||
if (instruction instanceof MethodCallInstruction) {
|
||||
reportCallMayProduceNpe(holder, (MethodCallInstruction)instruction);
|
||||
}
|
||||
else if (instruction instanceof FieldReferenceInstruction) {
|
||||
reportFieldAccessMayProduceNpe(holder, (FieldReferenceInstruction)instruction);
|
||||
}
|
||||
else if (instruction instanceof TypeCastInstruction) {
|
||||
reportCastMayFail(holder, (TypeCastInstruction)instruction);
|
||||
}
|
||||
else if (instruction instanceof BranchingInstruction) {
|
||||
handleBranchingInstruction(holder, visitor, trueSet, falseSet, reportedAnchors, (BranchingInstruction)instruction);
|
||||
}
|
||||
}
|
||||
|
||||
reportNullableArguments(runner, holder);
|
||||
reportNullableAssignments(runner, holder);
|
||||
reportUnboxedNullables(runner, holder);
|
||||
reportNullableReturns(runner, holder);
|
||||
reportNullableArgumentsPassedToNonAnnotated(runner, holder);
|
||||
}
|
||||
|
||||
private void reportNullableArgumentsPassedToNonAnnotated(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
Set<PsiExpression> exprs = runner.getNullableArgumentsPassedToNonAnnotatedParam();
|
||||
for (PsiExpression expr : exprs) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? "Passing <code>null</code> argument to non annotated parameter"
|
||||
: "Argument <code>#ref</code> #loc might be null but passed to non annotated parameter";
|
||||
LocalQuickFix[] fixes = createNPEFixes(expr, expr);
|
||||
final PsiElement parent = expr.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final int idx = ArrayUtil.find(((PsiExpressionList)parent).getExpressions(), expr);
|
||||
if (idx > -1) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiCallExpression) {
|
||||
final PsiMethod psiMethod = ((PsiCallExpression)gParent).resolveMethod();
|
||||
if (psiMethod != null && psiMethod.getManager().isInProject(psiMethod) && AnnotationUtil.isAnnotatingApplicable(psiMethod)) {
|
||||
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
|
||||
if (idx < parameters.length) {
|
||||
final AddNullableAnnotationFix addNullableAnnotationFix = new AddNullableAnnotationFix(parameters[idx]);
|
||||
fixes = fixes == null ? new LocalQuickFix[]{addNullableAnnotationFix} : ArrayUtil.append(fixes, addNullableAnnotationFix);
|
||||
holder.registerProblem(expr, text, fixes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void reportCallMayProduceNpe(ProblemsHolder holder, MethodCallInstruction mcInstruction) {
|
||||
if (mcInstruction.getCallExpression() instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression callExpression = (PsiMethodCallExpression)mcInstruction.getCallExpression();
|
||||
LocalQuickFix[] fix = createNPEFixes(callExpression.getMethodExpression().getQualifierExpression(), callExpression);
|
||||
|
||||
holder.registerProblem(callExpression,
|
||||
InspectionsBundle.message("dataflow.message.npe.method.invocation"),
|
||||
fix);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportFieldAccessMayProduceNpe(ProblemsHolder holder, FieldReferenceInstruction frInstruction) {
|
||||
PsiElement elementToAssert = frInstruction.getElementToAssert();
|
||||
PsiExpression expression = frInstruction.getExpression();
|
||||
if (expression instanceof PsiArrayAccessExpression) {
|
||||
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression);
|
||||
holder.registerProblem(expression,
|
||||
InspectionsBundle.message("dataflow.message.npe.array.access"),
|
||||
fix);
|
||||
}
|
||||
else {
|
||||
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression);
|
||||
holder.registerProblem(elementToAssert,
|
||||
InspectionsBundle.message("dataflow.message.npe.field.access"),
|
||||
fix);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportCastMayFail(ProblemsHolder holder, TypeCastInstruction instruction) {
|
||||
PsiTypeCastExpression typeCast = instruction.getCastExpression();
|
||||
holder.registerProblem(typeCast.getCastType(),
|
||||
InspectionsBundle.message("dataflow.message.cce", typeCast.getOperand().getText()));
|
||||
}
|
||||
|
||||
private void handleBranchingInstruction(ProblemsHolder holder,
|
||||
StandardInstructionVisitor visitor,
|
||||
Set<Instruction> trueSet,
|
||||
Set<Instruction> falseSet, HashSet<PsiElement> reportedAnchors, BranchingInstruction instruction) {
|
||||
PsiElement psiAnchor = instruction.getPsiAnchor();
|
||||
boolean underBinary = isAtRHSOfBooleanAnd(psiAnchor);
|
||||
if (instruction instanceof InstanceofInstruction && visitor.isInstanceofRedundant((InstanceofInstruction)instruction)) {
|
||||
if (visitor.canBeNull((BinopInstruction)instruction)) {
|
||||
holder.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.redundant.instanceof"),
|
||||
new RedundantInstanceofFix());
|
||||
}
|
||||
else {
|
||||
final LocalQuickFix localQuickFix = createSimplifyBooleanExpressionFix(psiAnchor, true);
|
||||
holder.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message(underBinary ? "dataflow.message.constant.condition.when.reached" : "dataflow.message.constant.condition", Boolean.toString(true)),
|
||||
localQuickFix == null ? null : new LocalQuickFix[]{localQuickFix});
|
||||
}
|
||||
}
|
||||
else if (psiAnchor instanceof PsiSwitchLabelStatement) {
|
||||
if (falseSet.contains(instruction)) {
|
||||
holder.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.unreachable.switch.label"));
|
||||
}
|
||||
}
|
||||
else if (psiAnchor != null && !reportedAnchors.contains(psiAnchor) && !isCompileConstantInIfCondition(psiAnchor)) {
|
||||
boolean evaluatesToTrue = trueSet.contains(instruction);
|
||||
if (onTheLeftSideOfConditionalAssignemnt(psiAnchor)) {
|
||||
holder.registerProblem(
|
||||
psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
|
||||
createSimplifyToAssignmentFix()
|
||||
);
|
||||
}
|
||||
else if (!skipReportingConstantCondition(visitor, psiAnchor, evaluatesToTrue)) {
|
||||
final LocalQuickFix fix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue);
|
||||
String message = InspectionsBundle.message(underBinary ?
|
||||
"dataflow.message.constant.condition.when.reached" :
|
||||
"dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
|
||||
holder.registerProblem(psiAnchor, message, fix == null ? null : new LocalQuickFix[]{fix});
|
||||
}
|
||||
reportedAnchors.add(psiAnchor);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean skipReportingConstantCondition(StandardInstructionVisitor visitor, PsiElement psiAnchor, boolean evaluatesToTrue) {
|
||||
return DONT_REPORT_TRUE_ASSERT_STATEMENTS && isAssertionEffectively(psiAnchor, evaluatesToTrue) ||
|
||||
visitor.silenceConstantCondition(psiAnchor);
|
||||
}
|
||||
|
||||
private void reportNullableArguments(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
Set<PsiExpression> exprs = runner.getNullableArguments();
|
||||
for (PsiExpression expr : exprs) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.passing.null.argument")
|
||||
: InspectionsBundle.message("dataflow.message.passing.nullable.argument");
|
||||
LocalQuickFix[] fixes = createNPEFixes(expr, expr);
|
||||
holder.registerProblem(expr, text, fixes);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportNullableAssignments(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
for (PsiExpression expr : runner.getNullableAssignments()) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.assigning.null")
|
||||
: InspectionsBundle.message("dataflow.message.assigning.nullable");
|
||||
holder.registerProblem(expr, text);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportUnboxedNullables(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
for (PsiExpression expr : runner.getUnboxedNullables()) {
|
||||
holder.registerProblem(expr, InspectionsBundle.message("dataflow.message.unboxing"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportNullableReturns(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
for (PsiReturnStatement statement : runner.getNullableReturns()) {
|
||||
final PsiExpression expr = statement.getReturnValue();
|
||||
if (runner.isInNotNullMethod()) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.return.null.from.notnull")
|
||||
: InspectionsBundle.message("dataflow.message.return.nullable.from.notnull");
|
||||
holder.registerProblem(expr, text);
|
||||
}
|
||||
else if (AnnotationUtil.isAnnotatingApplicable(statement)) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.return.null.from.notnullable")
|
||||
: InspectionsBundle.message("dataflow.message.return.nullable.from.notnullable");
|
||||
final NullableNotNullManager manager = NullableNotNullManager.getInstance(expr.getProject());
|
||||
holder.registerProblem(expr, text, new AnnotateMethodFix(manager.getDefaultNullable(), ArrayUtil.toStringArray(manager.getNotNulls())){
|
||||
@Override
|
||||
public int shouldAnnotateBaseMethod(PsiMethod method, PsiMethod superMethod, Project project) {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAssertionEffectively(PsiElement psiAnchor, boolean evaluatesToTrue) {
|
||||
PsiElement parent = psiAnchor.getParent();
|
||||
if (parent instanceof PsiAssertStatement) {
|
||||
return evaluatesToTrue;
|
||||
}
|
||||
if (parent instanceof PsiIfStatement && psiAnchor == ((PsiIfStatement)parent).getCondition()) {
|
||||
PsiStatement thenBranch = ((PsiIfStatement)parent).getThenBranch();
|
||||
if (thenBranch instanceof PsiThrowStatement) {
|
||||
return !evaluatesToTrue;
|
||||
}
|
||||
if (thenBranch instanceof PsiBlockStatement) {
|
||||
PsiStatement[] statements = ((PsiBlockStatement)thenBranch).getCodeBlock().getStatements();
|
||||
if (statements.length == 1 && statements[0] instanceof PsiThrowStatement) {
|
||||
return !evaluatesToTrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isAtRHSOfBooleanAnd(PsiElement expr) {
|
||||
PsiElement cur = expr;
|
||||
|
||||
while (cur != null && !(cur instanceof PsiMember)) {
|
||||
PsiElement parent = cur.getParent();
|
||||
|
||||
if (parent instanceof PsiBinaryExpression && cur == ((PsiBinaryExpression)parent).getROperand()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cur = parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isCompileConstantInIfCondition(PsiElement element) {
|
||||
if (!(element instanceof PsiReferenceExpression)) return false;
|
||||
PsiElement resolved = ((PsiReferenceExpression)element).resolve();
|
||||
if (!(resolved instanceof PsiField)) return false;
|
||||
PsiField field = (PsiField)resolved;
|
||||
|
||||
if (!field.hasModifierProperty(PsiModifier.FINAL)) return false;
|
||||
if (!field.hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiPrefixExpression && ((PsiPrefixExpression)parent).getOperationTokenType() == JavaTokenType.EXCL) {
|
||||
element = parent;
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return parent instanceof PsiIfStatement && ((PsiIfStatement)parent).getCondition() == element;
|
||||
}
|
||||
|
||||
private static boolean isNullLiteralExpression(PsiExpression expr) {
|
||||
if (expr instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression literalExpression = (PsiLiteralExpression)expr;
|
||||
return PsiType.NULL.equals(literalExpression.getType());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean onTheLeftSideOfConditionalAssignemnt(final PsiElement psiAnchor) {
|
||||
final PsiElement parent = psiAnchor.getParent();
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiAssignmentExpression expression = (PsiAssignmentExpression)parent;
|
||||
if (expression.getLExpression() == psiAnchor) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LocalQuickFix createSimplifyBooleanExpressionFix(PsiElement element, final boolean value) {
|
||||
SimplifyBooleanExpressionFix fix = createIntention(element, value);
|
||||
if (fix == null) return null;
|
||||
final String text = fix.getText();
|
||||
return new LocalQuickFix() {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement psiElement = descriptor.getPsiElement();
|
||||
if (psiElement == null) return;
|
||||
final SimplifyBooleanExpressionFix fix = createIntention(psiElement, value);
|
||||
if (fix == null) return;
|
||||
try {
|
||||
LOG.assertTrue(psiElement.isValid());
|
||||
fix.applyFix();
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.simplify.boolean.expression.quickfix");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LocalQuickFix createSimplifyToAssignmentFix() {
|
||||
return new LocalQuickFix() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.simplify.to.assignment.quickfix.name");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.simplify.boolean.expression.quickfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement psiElement = descriptor.getPsiElement();
|
||||
if (psiElement == null) return;
|
||||
|
||||
final PsiAssignmentExpression assignmentExpression = PsiTreeUtil.getParentOfType(psiElement, PsiAssignmentExpression.class);
|
||||
if (assignmentExpression == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
final String lExpressionText = assignmentExpression.getLExpression().getText();
|
||||
final PsiExpression rExpression = assignmentExpression.getRExpression();
|
||||
final String rExpressionText = rExpression != null ? rExpression.getText() : "";
|
||||
assignmentExpression.replace(factory.createExpressionFromText(lExpressionText + " = " + rExpressionText, psiElement));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static SimplifyBooleanExpressionFix createIntention(PsiElement element, boolean value) {
|
||||
if (!(element instanceof PsiExpression)) return null;
|
||||
final PsiExpression expression = (PsiExpression)element;
|
||||
while (element.getParent() instanceof PsiExpression) {
|
||||
element = element.getParent();
|
||||
}
|
||||
final SimplifyBooleanExpressionFix fix = new SimplifyBooleanExpressionFix(expression, value);
|
||||
// simplify intention already active
|
||||
if (!fix.isAvailable() ||
|
||||
SimplifyBooleanExpressionFix.canBeSimplified((PsiExpression)element)) {
|
||||
return null;
|
||||
}
|
||||
return fix;
|
||||
}
|
||||
|
||||
private static class RedundantInstanceofFix implements LocalQuickFix {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.redundant.instanceof.quickfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(descriptor.getPsiElement())) return;
|
||||
final PsiElement psiElement = descriptor.getPsiElement();
|
||||
if (psiElement instanceof PsiInstanceOfExpression) {
|
||||
try {
|
||||
final PsiExpression compareToNull = JavaPsiFacade.getInstance(psiElement.getProject()).getElementFactory().
|
||||
createExpressionFromText(((PsiInstanceOfExpression)psiElement).getOperand().getText() + " != null", psiElement.getParent());
|
||||
psiElement.replace(compareToNull);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.display.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return GroupNames.BUGS_GROUP_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getShortName() {
|
||||
return SHORT_NAME;
|
||||
}
|
||||
|
||||
private static class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
private final StandardDataFlowRunner myRunner;
|
||||
|
||||
private DataFlowInstructionVisitor(StandardDataFlowRunner runner) {
|
||||
myRunner = runner;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAssigningToNotNullableVariable(AssignInstruction instruction) {
|
||||
myRunner.onAssigningToNotNullableVariable(instruction.getRExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNullableReturn(CheckReturnValueInstruction instruction) {
|
||||
myRunner.onNullableReturn(instruction.getReturn());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstructionProducesCCE(TypeCastInstruction instruction) {
|
||||
myRunner.onInstructionProducesCCE(instruction);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstructionProducesNPE(Instruction instruction) {
|
||||
if (instruction instanceof MethodCallInstruction &&
|
||||
((MethodCallInstruction)instruction).getMethodType() == MethodCallInstruction.MethodType.UNBOXING) {
|
||||
myRunner.onUnboxingNullable(((MethodCallInstruction)instruction).getContext());
|
||||
}
|
||||
else {
|
||||
myRunner.onInstructionProducesNPE(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPassingNullParameter(PsiExpression arg) {
|
||||
myRunner.onPassingNullParameter(arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPassingNullParameterToNonAnnotated(DataFlowRunner runner, PsiExpression arg) {
|
||||
myRunner.onPassingNullParameterToNonAnnotated(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -19,7 +19,7 @@
|
||||
* User: max
|
||||
* Date: Jan 28, 2002
|
||||
* Time: 10:16:39 PM
|
||||
* To change template for new class use
|
||||
* To change template for new class use
|
||||
* Code Style | Class Templates options (Tools | IDE Options).
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
@@ -73,7 +73,7 @@ public class DataFlowRunner {
|
||||
PsiClass containingClass = PsiTreeUtil.getParentOfType(psiBlock, PsiClass.class);
|
||||
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) {
|
||||
final PsiElement parent = containingClass.getParent();
|
||||
final PsiCodeBlock block = DfaUtil.getTopmostBlockInSameClass(parent);
|
||||
final PsiCodeBlock block = DfaPsiUtil.getTopmostBlockInSameClass(parent);
|
||||
if ((parent instanceof PsiNewExpression || parent instanceof PsiDeclarationStatement) && block != null) {
|
||||
final EnvironmentalInstructionVisitor envVisitor = new EnvironmentalInstructionVisitor(visitor, parent);
|
||||
final RunnerResult result = analyzeMethod(block, envVisitor);
|
||||
@@ -257,7 +257,7 @@ public class DataFlowRunner {
|
||||
private void checkEnvironment(DataFlowRunner runner, DfaMemoryState memState, @Nullable PsiElement anchor) {
|
||||
if (myClassParent == anchor) {
|
||||
DfaMemoryStateImpl copy = (DfaMemoryStateImpl)memState.createCopy();
|
||||
copy.flushFields(runner);
|
||||
copy.flushFields(runner.getFields());
|
||||
Set<DfaVariableValue> vars = new HashSet<DfaVariableValue>(copy.getVariableStates().keySet());
|
||||
for (DfaVariableValue value : vars) {
|
||||
copy.flushDependencies(value);
|
||||
+1
-1
@@ -47,7 +47,7 @@ public interface DfaMemoryState {
|
||||
|
||||
boolean applyNotNull(DfaValue value);
|
||||
|
||||
void flushFields(DataFlowRunner runner);
|
||||
void flushFields(DfaVariableValue[] fields);
|
||||
|
||||
void flushVariable(DfaVariableValue variable);
|
||||
|
||||
+7
-7
@@ -42,7 +42,7 @@ import java.util.*;
|
||||
public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
private final DfaValueFactory myFactory;
|
||||
|
||||
private final ArrayList<SortedIntSet> myEqClasses = new ArrayList<SortedIntSet>();
|
||||
private final List<SortedIntSet> myEqClasses = new ArrayList<SortedIntSet>();
|
||||
private int myStateSize = 0;
|
||||
private final Stack<DfaValue> myStack = new Stack<DfaValue>();
|
||||
private TIntStack myOffsetStack = new TIntStack(1);
|
||||
@@ -691,7 +691,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
|
||||
private static boolean isMaybeBoxedConstant(DfaValue val) {
|
||||
return val instanceof DfaConstValue ||
|
||||
(val instanceof DfaBoxedValue && ((DfaBoxedValue)val).getWrappedValue() instanceof DfaConstValue);
|
||||
val instanceof DfaBoxedValue && ((DfaBoxedValue)val).getWrappedValue() instanceof DfaConstValue;
|
||||
}
|
||||
|
||||
private boolean checkCompareWithBooleanLiteral(DfaValue dfaLeft, DfaValue dfaRight, boolean negated) {
|
||||
@@ -788,7 +788,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
state.setNullable(false);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
myVariableStates.put(dfaVar, state);
|
||||
}
|
||||
|
||||
@@ -804,16 +804,16 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushFields(DataFlowRunner runner) {
|
||||
public void flushFields(DfaVariableValue[] fields) {
|
||||
Set<DfaVariableValue> allVars = new HashSet<DfaVariableValue>(myVariableStates.keySet());
|
||||
Collections.addAll(allVars, runner.getFields());
|
||||
|
||||
Collections.addAll(allVars, fields);
|
||||
|
||||
Set<DfaVariableValue> dependencies = new HashSet<DfaVariableValue>();
|
||||
for (DfaVariableValue variableValue : allVars) {
|
||||
dependencies.addAll(myFactory.getVarFactory().getAllQualifiedBy(variableValue));
|
||||
}
|
||||
allVars.addAll(dependencies);
|
||||
|
||||
|
||||
for (DfaVariableValue value : allVars) {
|
||||
if (myVariableStates.containsKey(value) || getEqClassIndex(value) >= 0) {
|
||||
if (value.isFlushableByCalls()) {
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.NullableFunction;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class DfaPsiUtil {
|
||||
public static boolean isPlainMutableField(PsiVariable var) {
|
||||
return !var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && !var.hasModifierProperty(PsiModifier.VOLATILE) && var instanceof PsiField;
|
||||
}
|
||||
|
||||
public static boolean isFinalField(PsiVariable var) {
|
||||
return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField;
|
||||
}
|
||||
|
||||
static PsiElement getEnclosingCodeBlock(final PsiVariable variable, final PsiElement context) {
|
||||
PsiElement codeBlock;
|
||||
if (variable instanceof PsiParameter) {
|
||||
codeBlock = ((PsiParameter)variable).getDeclarationScope();
|
||||
if (codeBlock instanceof PsiMethod) {
|
||||
codeBlock = ((PsiMethod)codeBlock).getBody();
|
||||
}
|
||||
}
|
||||
else if (variable instanceof PsiLocalVariable) {
|
||||
codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
|
||||
}
|
||||
else {
|
||||
codeBlock = PsiTreeUtil.getParentOfType(context, PsiCodeBlock.class);
|
||||
}
|
||||
while (codeBlock != null) {
|
||||
PsiAnonymousClass anon = PsiTreeUtil.getParentOfType(codeBlock, PsiAnonymousClass.class);
|
||||
if (anon == null) break;
|
||||
codeBlock = PsiTreeUtil.getParentOfType(anon, PsiCodeBlock.class);
|
||||
}
|
||||
return codeBlock;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Nullness getElementNullability(@Nullable PsiType resultType, @Nullable PsiModifierListOwner owner) {
|
||||
if (owner == null) {
|
||||
return Nullness.UNKNOWN;
|
||||
}
|
||||
|
||||
if (NullableNotNullManager.isNullable(owner)) {
|
||||
return Nullness.NULLABLE;
|
||||
}
|
||||
if (NullableNotNullManager.isNotNull(owner)) {
|
||||
return Nullness.NOT_NULL;
|
||||
}
|
||||
|
||||
if (resultType != null) {
|
||||
NullableNotNullManager nnn = NullableNotNullManager.getInstance(owner.getProject());
|
||||
for (PsiAnnotation annotation : resultType.getAnnotations()) {
|
||||
String qualifiedName = annotation.getQualifiedName();
|
||||
if (nnn.getNullables().contains(qualifiedName)) {
|
||||
return Nullness.NULLABLE;
|
||||
}
|
||||
if (nnn.getNotNulls().contains(qualifiedName)) {
|
||||
return Nullness.NOT_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Nullness.UNKNOWN;
|
||||
}
|
||||
|
||||
public static List<PsiExpression> findAllConstructorInitializers(PsiField field) {
|
||||
final List<PsiExpression> result = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
ContainerUtil.addIfNotNull(result, field.getInitializer());
|
||||
|
||||
PsiClass containingClass = field.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
LocalSearchScope scope = new LocalSearchScope(containingClass.getConstructors());
|
||||
ReferencesSearch.search(field, scope, false).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
final PsiAssignmentExpression assignment = getAssignmentExpressionIfOnAssignmentLhs(element);
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(assignment, PsiMethod.class);
|
||||
if (method != null && method.isConstructor() && assignment != null) {
|
||||
ContainerUtil.addIfNotNull(result, assignment.getRExpression());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiAssignmentExpression getAssignmentExpressionIfOnAssignmentLhs(PsiElement expression) {
|
||||
PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, PsiParenthesizedExpression.class);
|
||||
if (!(parent instanceof PsiAssignmentExpression)) {
|
||||
return null;
|
||||
}
|
||||
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
|
||||
if (!PsiTreeUtil.isAncestor(assignmentExpression.getLExpression(), expression, false)) {
|
||||
return null;
|
||||
}
|
||||
return assignmentExpression;
|
||||
}
|
||||
|
||||
public static boolean isNullableInitialized(PsiVariable var, boolean nullable) {
|
||||
if (!isFinalField(var)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<PsiExpression> initializers = findAllConstructorInitializers((PsiField)var);
|
||||
if (initializers.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (PsiExpression expression : initializers) {
|
||||
if (!(expression instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
PsiElement target = ((PsiReferenceExpression)expression).resolve();
|
||||
if (!(target instanceof PsiParameter)) {
|
||||
return false;
|
||||
}
|
||||
if (nullable && NullableNotNullManager.isNullable((PsiParameter)target)) {
|
||||
return true;
|
||||
}
|
||||
if (!nullable && !NullableNotNullManager.isNotNull((PsiParameter)target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !nullable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiCodeBlock getTopmostBlockInSameClass(@NotNull PsiElement position) {
|
||||
PsiCodeBlock block = PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, false, PsiMember.class, PsiFile.class);
|
||||
if (block == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiCodeBlock lastBlock = block;
|
||||
while (true) {
|
||||
block = PsiTreeUtil.getParentOfType(block, PsiCodeBlock.class, true, PsiMember.class, PsiFile.class);
|
||||
if (block == null) {
|
||||
return lastBlock;
|
||||
}
|
||||
lastBlock = block;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<PsiExpression> getVariableAssignmentsInFile(@NotNull PsiVariable psiVariable,
|
||||
final boolean literalsOnly,
|
||||
final PsiElement place) {
|
||||
final Ref<Boolean> modificationRef = Ref.create(Boolean.FALSE);
|
||||
final PsiCodeBlock codeBlock = place == null? null : getTopmostBlockInSameClass(place);
|
||||
final int placeOffset = codeBlock != null? place.getTextRange().getStartOffset() : 0;
|
||||
List<PsiExpression> list = ContainerUtil.mapNotNull(
|
||||
ReferencesSearch.search(psiVariable, new LocalSearchScope(new PsiElement[] {psiVariable.getContainingFile()}, null, true)).findAll(),
|
||||
new NullableFunction<PsiReference, PsiExpression>() {
|
||||
@Override
|
||||
public PsiExpression fun(final PsiReference psiReference) {
|
||||
if (modificationRef.get()) return null;
|
||||
final PsiElement parent = psiReference.getElement().getParent();
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
|
||||
final IElementType operation = assignmentExpression.getOperationTokenType();
|
||||
if (assignmentExpression.getLExpression() == psiReference) {
|
||||
if (JavaTokenType.EQ.equals(operation)) {
|
||||
final PsiExpression rValue = assignmentExpression.getRExpression();
|
||||
if (!literalsOnly || allOperandsAreLiterals(rValue)) {
|
||||
// if there's a codeBlock omit the values assigned later
|
||||
if (codeBlock != null && PsiTreeUtil.isAncestor(codeBlock, parent, true)
|
||||
&& placeOffset < parent.getTextRange().getStartOffset()) {
|
||||
return null;
|
||||
}
|
||||
return rValue;
|
||||
}
|
||||
else {
|
||||
modificationRef.set(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
else if (JavaTokenType.PLUSEQ.equals(operation)) {
|
||||
modificationRef.set(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (modificationRef.get()) return Collections.emptyList();
|
||||
PsiExpression initializer = psiVariable.getInitializer();
|
||||
if (initializer != null && (!literalsOnly || allOperandsAreLiterals(initializer))) {
|
||||
list = ContainerUtil.concat(list, Collections.singletonList(initializer));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean allOperandsAreLiterals(@Nullable final PsiExpression expression) {
|
||||
if (expression == null) return false;
|
||||
if (expression instanceof PsiLiteralExpression) return true;
|
||||
if (expression instanceof PsiPolyadicExpression) {
|
||||
Stack<PsiExpression> stack = new Stack<PsiExpression>();
|
||||
stack.add(expression);
|
||||
while (!stack.isEmpty()) {
|
||||
PsiExpression psiExpression = stack.pop();
|
||||
if (psiExpression instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)psiExpression;
|
||||
for (PsiExpression op : binaryExpression.getOperands()) {
|
||||
stack.push(op);
|
||||
}
|
||||
}
|
||||
else if (!(psiExpression instanceof PsiLiteralExpression)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+3
-187
@@ -15,27 +15,18 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.PushInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.codeInspection.nullable.NullableStuffInspection;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.MultiValuesMap;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.CachedValue;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.NullableFunction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -58,7 +49,7 @@ public class DfaUtil {
|
||||
|
||||
CachedValue<MultiValuesMap<PsiVariable, PsiExpression>> cachedValue = context.getUserData(DFA_VARIABLE_INFO_KEY);
|
||||
if (cachedValue == null) {
|
||||
final PsiElement codeBlock = getEnclosingCodeBlock(variable, context);
|
||||
final PsiElement codeBlock = DfaPsiUtil.getEnclosingCodeBlock(variable, context);
|
||||
cachedValue = CachedValuesManager.getManager(context.getProject()).createCachedValue(new CachedValueProvider<MultiValuesMap<PsiVariable, PsiExpression>>() {
|
||||
@Override
|
||||
public Result<MultiValuesMap<PsiVariable, PsiExpression>> compute() {
|
||||
@@ -87,76 +78,11 @@ public class DfaUtil {
|
||||
return expressions == null ? Collections.<PsiExpression>emptyList() : expressions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Nullness getElementNullability(@Nullable PsiType resultType, @Nullable PsiModifierListOwner owner) {
|
||||
if (owner == null) {
|
||||
return Nullness.UNKNOWN;
|
||||
}
|
||||
|
||||
if (NullableNotNullManager.isNullable(owner)) {
|
||||
return Nullness.NULLABLE;
|
||||
}
|
||||
if (NullableNotNullManager.isNotNull(owner)) {
|
||||
return Nullness.NOT_NULL;
|
||||
}
|
||||
|
||||
if (resultType != null) {
|
||||
NullableNotNullManager nnn = NullableNotNullManager.getInstance(owner.getProject());
|
||||
for (PsiAnnotation annotation : resultType.getAnnotations()) {
|
||||
String qualifiedName = annotation.getQualifiedName();
|
||||
if (nnn.getNullables().contains(qualifiedName)) {
|
||||
return Nullness.NULLABLE;
|
||||
}
|
||||
if (nnn.getNotNulls().contains(qualifiedName)) {
|
||||
return Nullness.NOT_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Nullness.UNKNOWN;
|
||||
}
|
||||
|
||||
public static boolean isNullableInitialized(PsiVariable var, boolean nullable) {
|
||||
if (!isFinalField(var)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<PsiExpression> initializers = NullableStuffInspection.findAllConstructorInitializers((PsiField)var);
|
||||
if (initializers.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (PsiExpression expression : initializers) {
|
||||
if (!(expression instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
PsiElement target = ((PsiReferenceExpression)expression).resolve();
|
||||
if (!(target instanceof PsiParameter)) {
|
||||
return false;
|
||||
}
|
||||
if (nullable && NullableNotNullManager.isNullable((PsiParameter)target)) {
|
||||
return true;
|
||||
}
|
||||
if (!nullable && !NullableNotNullManager.isNotNull((PsiParameter)target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !nullable;
|
||||
}
|
||||
|
||||
public static boolean isPlainMutableField(PsiVariable var) {
|
||||
return !var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && !var.hasModifierProperty(PsiModifier.VOLATILE) && var instanceof PsiField;
|
||||
}
|
||||
|
||||
public static boolean isFinalField(PsiVariable var) {
|
||||
return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Nullness checkNullness(@Nullable final PsiVariable variable, @Nullable final PsiElement context) {
|
||||
if (variable == null || context == null) return Nullness.UNKNOWN;
|
||||
|
||||
final PsiElement codeBlock = getEnclosingCodeBlock(variable, context);
|
||||
final PsiElement codeBlock = DfaPsiUtil.getEnclosingCodeBlock(variable, context);
|
||||
if (codeBlock == null) {
|
||||
return Nullness.UNKNOWN;
|
||||
}
|
||||
@@ -170,45 +96,6 @@ public class DfaUtil {
|
||||
return Nullness.UNKNOWN;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiCodeBlock getTopmostBlockInSameClass(@NotNull PsiElement position) {
|
||||
PsiCodeBlock block = PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, false, PsiMember.class, PsiFile.class);
|
||||
if (block == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiCodeBlock lastBlock = block;
|
||||
while (true) {
|
||||
block = PsiTreeUtil.getParentOfType(block, PsiCodeBlock.class, true, PsiMember.class, PsiFile.class);
|
||||
if (block == null) {
|
||||
return lastBlock;
|
||||
}
|
||||
lastBlock = block;
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiElement getEnclosingCodeBlock(final PsiVariable variable, final PsiElement context) {
|
||||
PsiElement codeBlock;
|
||||
if (variable instanceof PsiParameter) {
|
||||
codeBlock = ((PsiParameter)variable).getDeclarationScope();
|
||||
if (codeBlock instanceof PsiMethod) {
|
||||
codeBlock = ((PsiMethod)codeBlock).getBody();
|
||||
}
|
||||
}
|
||||
else if (variable instanceof PsiLocalVariable) {
|
||||
codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
|
||||
}
|
||||
else {
|
||||
codeBlock = PsiTreeUtil.getParentOfType(context, PsiCodeBlock.class);
|
||||
}
|
||||
while (codeBlock != null) {
|
||||
PsiAnonymousClass anon = PsiTreeUtil.getParentOfType(codeBlock, PsiAnonymousClass.class);
|
||||
if (anon == null) break;
|
||||
codeBlock = PsiTreeUtil.getParentOfType(anon, PsiCodeBlock.class);
|
||||
}
|
||||
return codeBlock;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<? extends PsiElement> getPossibleInitializationElements(final PsiElement qualifierExpression) {
|
||||
if (qualifierExpression instanceof PsiMethodCallExpression) {
|
||||
@@ -221,7 +108,7 @@ public class DfaUtil {
|
||||
}
|
||||
final Collection<? extends PsiElement> variableValues = getCachedVariableValues((PsiVariable)targetElement, qualifierExpression);
|
||||
if (variableValues == null || variableValues.isEmpty()) {
|
||||
return getVariableAssignmentsInFile((PsiVariable)targetElement, false, qualifierExpression);
|
||||
return DfaPsiUtil.getVariableAssignmentsInFile((PsiVariable)targetElement, false, qualifierExpression);
|
||||
}
|
||||
return variableValues;
|
||||
}
|
||||
@@ -231,77 +118,6 @@ public class DfaUtil {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<PsiExpression> getVariableAssignmentsInFile(@NotNull PsiVariable psiVariable,
|
||||
final boolean literalsOnly,
|
||||
final PsiElement place) {
|
||||
final Ref<Boolean> modificationRef = Ref.create(Boolean.FALSE);
|
||||
final PsiCodeBlock codeBlock = place == null? null : getTopmostBlockInSameClass(place);
|
||||
final int placeOffset = codeBlock != null? place.getTextRange().getStartOffset() : 0;
|
||||
List<PsiExpression> list = ContainerUtil.mapNotNull(
|
||||
ReferencesSearch.search(psiVariable, new LocalSearchScope(new PsiElement[] {psiVariable.getContainingFile()}, null, true)).findAll(),
|
||||
new NullableFunction<PsiReference, PsiExpression>() {
|
||||
@Override
|
||||
public PsiExpression fun(final PsiReference psiReference) {
|
||||
if (modificationRef.get()) return null;
|
||||
final PsiElement parent = psiReference.getElement().getParent();
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
|
||||
final IElementType operation = assignmentExpression.getOperationTokenType();
|
||||
if (assignmentExpression.getLExpression() == psiReference) {
|
||||
if (JavaTokenType.EQ.equals(operation)) {
|
||||
final PsiExpression rValue = assignmentExpression.getRExpression();
|
||||
if (!literalsOnly || allOperandsAreLiterals(rValue)) {
|
||||
// if there's a codeBlock omit the values assigned later
|
||||
if (codeBlock != null && PsiTreeUtil.isAncestor(codeBlock, parent, true)
|
||||
&& placeOffset < parent.getTextRange().getStartOffset()) {
|
||||
return null;
|
||||
}
|
||||
return rValue;
|
||||
}
|
||||
else {
|
||||
modificationRef.set(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
else if (JavaTokenType.PLUSEQ.equals(operation)) {
|
||||
modificationRef.set(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (modificationRef.get()) return Collections.emptyList();
|
||||
PsiExpression initializer = psiVariable.getInitializer();
|
||||
if (initializer != null && (!literalsOnly || allOperandsAreLiterals(initializer))) {
|
||||
list = ContainerUtil.concat(list, Collections.singletonList(initializer));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean allOperandsAreLiterals(@Nullable final PsiExpression expression) {
|
||||
if (expression == null) return false;
|
||||
if (expression instanceof PsiLiteralExpression) return true;
|
||||
if (expression instanceof PsiPolyadicExpression) {
|
||||
Stack<PsiExpression> stack = new Stack<PsiExpression>();
|
||||
stack.add(expression);
|
||||
while (!stack.isEmpty()) {
|
||||
PsiExpression psiExpression = stack.pop();
|
||||
if (psiExpression instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)psiExpression;
|
||||
for (PsiExpression op : binaryExpression.getOperands()) {
|
||||
stack.push(op);
|
||||
}
|
||||
}
|
||||
else if (!(psiExpression instanceof PsiLiteralExpression)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class ValuableInstructionVisitor extends StandardInstructionVisitor {
|
||||
final MultiValuesMap<PsiVariable, PsiExpression> myValues = new MultiValuesMap<PsiVariable, PsiExpression>(true);
|
||||
final Set<PsiVariable> myNulls = new THashSet<PsiVariable>();
|
||||
+1
-1
@@ -121,7 +121,7 @@ public abstract class InstructionVisitor {
|
||||
if (variable != null) {
|
||||
memState.flushVariable(variable);
|
||||
} else {
|
||||
memState.flushFields(runner);
|
||||
memState.flushFields(runner.getFields());
|
||||
}
|
||||
return nextInstruction(instruction, runner, memState);
|
||||
}
|
||||
+5
-5
@@ -55,7 +55,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return Nullness.NOT_NULL;
|
||||
}
|
||||
|
||||
return callExpression != null ? DfaUtil.getElementNullability(key.getResultType(), callExpression.resolveMethod()) : null;
|
||||
return callExpression != null ? DfaPsiUtil.getElementNullability(key.getResultType(), callExpression.resolveMethod()) : null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,7 +74,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
|
||||
Map<PsiExpression, Nullness> map = ContainerUtil.newHashMap();
|
||||
for (int i = 0; i < checkedCount; i++) {
|
||||
map.put(args[i], DfaUtil.getElementNullability(substitutor.substitute(parameters[i].getType()), parameters[i]));
|
||||
map.put(args[i], DfaPsiUtil.getElementNullability(substitutor.substitute(parameters[i].getType()), parameters[i]));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
if (dfaDest instanceof DfaVariableValue) {
|
||||
DfaVariableValue var = (DfaVariableValue) dfaDest;
|
||||
final PsiVariable psiVariable = var.getPsiVariable();
|
||||
if (DfaUtil.getElementNullability(var.getVariableType(), psiVariable) == Nullness.NOT_NULL) {
|
||||
if (DfaPsiUtil.getElementNullability(var.getVariableType(), psiVariable) == Nullness.NOT_NULL) {
|
||||
if (!memState.applyNotNull(dfaSource)) {
|
||||
onAssigningToNotNullableVariable(instruction);
|
||||
}
|
||||
@@ -215,7 +215,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
finally {
|
||||
memState.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
|
||||
if (instruction.shouldFlushFields()) {
|
||||
memState.flushFields(runner);
|
||||
memState.flushFields(runner.getFields());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,7 +229,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
|
||||
final PsiType type = instruction.getResultType();
|
||||
final MethodCallInstruction.MethodType methodType = instruction.getMethodType();
|
||||
|
||||
|
||||
if (methodType == MethodCallInstruction.MethodType.UNBOXING) {
|
||||
return factory.getBoxedFactory().createUnboxed(qualifierValue);
|
||||
}
|
||||
+3
-2
@@ -19,7 +19,7 @@
|
||||
* User: max
|
||||
* Date: Jan 26, 2002
|
||||
* Time: 10:46:40 PM
|
||||
* To change template for new class use
|
||||
* To change template for new class use
|
||||
* Code Style | Class Templates options (Tools | IDE Options).
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow.instructions;
|
||||
@@ -31,10 +31,11 @@ import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Instruction {
|
||||
private int myIndex;
|
||||
private final ArrayList<DfaMemoryState> myProcessedStates;
|
||||
private final List<DfaMemoryState> myProcessedStates;
|
||||
|
||||
protected Instruction() {
|
||||
myProcessedStates = new ArrayList<DfaMemoryState>();
|
||||
+1
-3
@@ -13,10 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.guess.impl;
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
+5
-5
@@ -24,7 +24,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.DfaUtil;
|
||||
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.codeInspection.dataFlow.Nullness;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
@@ -159,11 +159,11 @@ public class DfaVariableValue extends DfaValue {
|
||||
}
|
||||
|
||||
PsiVariable var = getPsiVariable();
|
||||
Nullness nullability = DfaUtil.getElementNullability(getVariableType(), var);
|
||||
Nullness nullability = DfaPsiUtil.getElementNullability(getVariableType(), var);
|
||||
if (nullability == Nullness.UNKNOWN && var != null) {
|
||||
if (DfaUtil.isNullableInitialized(var, true)) {
|
||||
if (DfaPsiUtil.isNullableInitialized(var, true)) {
|
||||
nullability = Nullness.NULLABLE;
|
||||
} else if (DfaUtil.isNullableInitialized(var, false)) {
|
||||
} else if (DfaPsiUtil.isNullableInitialized(var, false)) {
|
||||
nullability = Nullness.NOT_NULL;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,7 @@ public class DfaVariableValue extends DfaValue {
|
||||
|
||||
return nullability;
|
||||
}
|
||||
|
||||
|
||||
public boolean isLocalVariable() {
|
||||
return myVariable instanceof PsiLocalVariable || myVariable instanceof PsiParameter;
|
||||
}
|
||||
+6
-4
@@ -17,7 +17,7 @@ package com.intellij.codeInspection.nullable;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationFix;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
@@ -25,12 +25,13 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiNameValuePair;
|
||||
import com.intellij.psi.PsiParameter;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -65,7 +66,7 @@ public class AnnotateOverriddenMethodParameterFix implements LocalQuickFix {
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class);
|
||||
if (method == null) return;
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
int index = ArrayUtil.find(parameters, parameter);
|
||||
int index = ArrayUtilRt.find(parameters, parameter);
|
||||
|
||||
List<PsiParameter> toAnnotate = new ArrayList<PsiParameter>();
|
||||
|
||||
@@ -84,7 +85,8 @@ public class AnnotateOverriddenMethodParameterFix implements LocalQuickFix {
|
||||
try {
|
||||
assert psiParam != null : toAnnotate;
|
||||
if (AnnotationUtil.isAnnotatingApplicable(psiParam, myAnnotation)) {
|
||||
new AddAnnotationFix(myAnnotation, psiParam, myAnnosToRemove).invoke(project, null, psiParam.getContainingFile());
|
||||
AddAnnotationPsiFix fix = new AddAnnotationPsiFix(myAnnotation, psiParam, PsiNameValuePair.EMPTY_ARRAY, myAnnosToRemove);
|
||||
fix.invoke(project, psiParam.getContainingFile(), psiParam, psiParam);
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
+2
-1
@@ -59,7 +59,8 @@ class ChangeNullableDefaultsFix implements LocalQuickFix {
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
if (myNotNullName != null) {
|
||||
myManager.setDefaultNotNull(myNotNullName);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
myManager.setDefaultNullable(myNullableName);
|
||||
}
|
||||
}
|
||||
+469
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.codeInspection.nullable;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
||||
import com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
// deprecated fields remain to minimize changes to users inspection profiles (which are often located in version control).
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL = true;
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL = true;
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE = true;
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL = true;
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_GETTER = true;
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_SETTER_PARAMETER = true;
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = true; // remains for test
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD = true;
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.nullable.NullableStuffInspectionBase");
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override public void visitMethod(PsiMethod method) {
|
||||
if (!PsiUtil.isLanguageLevel5OrHigher(method)) return;
|
||||
checkNullableStuffForMethod(method, holder);
|
||||
}
|
||||
|
||||
@Override public void visitField(PsiField field) {
|
||||
if (!PsiUtil.isLanguageLevel5OrHigher(field)) return;
|
||||
final PsiType type = field.getType();
|
||||
final Annotated annotated = check(field, holder, type);
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(type)) {
|
||||
return;
|
||||
}
|
||||
Project project = holder.getProject();
|
||||
final NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
|
||||
if (annotated.isDeclaredNotNull ^ annotated.isDeclaredNullable) {
|
||||
final String anno = annotated.isDeclaredNotNull ? manager.getDefaultNotNull() : manager.getDefaultNullable();
|
||||
final List<String> annoToRemove = annotated.isDeclaredNotNull ? manager.getNullables() : manager.getNotNulls();
|
||||
|
||||
if (!AnnotationUtil.isAnnotatingApplicable(field, anno)) {
|
||||
final PsiAnnotation notNull = AnnotationUtil.findAnnotation(field, manager.getNotNulls());
|
||||
final PsiAnnotation nullable = AnnotationUtil.findAnnotation(field, manager.getNullables());
|
||||
holder.registerProblem(field.getNameIdentifier(), "Nullable/NotNull defaults are not accessible in current context",
|
||||
new ChangeNullableDefaultsFix(notNull, nullable, manager));
|
||||
return;
|
||||
}
|
||||
|
||||
String propName = JavaCodeStyleManager.getInstance(project).variableNameToPropertyName(field.getName(), VariableKind.FIELD);
|
||||
final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
|
||||
final PsiMethod getter = PropertyUtil.findPropertyGetter(field.getContainingClass(), propName, isStatic, false);
|
||||
final String nullableSimpleName = StringUtil.getShortName(manager.getDefaultNullable());
|
||||
final String notNullSimpleName = StringUtil.getShortName(manager.getDefaultNotNull());
|
||||
final PsiIdentifier nameIdentifier = getter == null ? null : getter.getNameIdentifier();
|
||||
if (nameIdentifier != null && nameIdentifier.isPhysical()) {
|
||||
if (PropertyUtil.isSimpleGetter(getter)) {
|
||||
AnnotateMethodFix getterAnnoFix = new AnnotateMethodFix(anno, ArrayUtil.toStringArray(annoToRemove)) {
|
||||
@Override
|
||||
public int shouldAnnotateBaseMethod(PsiMethod method, PsiMethod superMethod, Project project) {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
if (REPORT_NOT_ANNOTATED_GETTER) {
|
||||
if (!AnnotationUtil.isAnnotated(getter, manager.getAllAnnotations(), false, false) &&
|
||||
!TypeConversionUtil.isPrimitiveAndNotNull(getter.getReturnType())) {
|
||||
holder.registerProblem(nameIdentifier, InspectionsBundle
|
||||
.message("inspection.nullable.problems.annotated.field.getter.not.annotated", StringUtil.getShortName(anno)),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, getterAnnoFix);
|
||||
}
|
||||
}
|
||||
if (annotated.isDeclaredNotNull && manager.isNullable(getter, false)) {
|
||||
holder.registerProblem(nameIdentifier, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.getter.conflict", StringUtil.getShortName(anno), nullableSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, getterAnnoFix);
|
||||
} else if (annotated.isDeclaredNullable && manager.isNotNull(getter, false)) {
|
||||
holder.registerProblem(nameIdentifier, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.getter.conflict", StringUtil.getShortName(anno), notNullSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, getterAnnoFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final PsiClass containingClass = field.getContainingClass();
|
||||
final PsiMethod setter = PropertyUtil.findPropertySetter(containingClass, propName, isStatic, false);
|
||||
if (setter != null) {
|
||||
final PsiParameter[] parameters = setter.getParameterList().getParameters();
|
||||
assert parameters.length == 1 : setter.getText();
|
||||
final PsiParameter parameter = parameters[0];
|
||||
LOG.assertTrue(parameter != null, setter.getText());
|
||||
AddAnnotationPsiFix addAnnoFix = new AddAnnotationPsiFix(anno, parameter, PsiNameValuePair.EMPTY_ARRAY, ArrayUtil.toStringArray(annoToRemove));
|
||||
if (REPORT_NOT_ANNOTATED_GETTER && !AnnotationUtil.isAnnotated(parameter, manager.getAllAnnotations(), false, false) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) {
|
||||
final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
|
||||
assertValidElement(setter, parameter, nameIdentifier1);
|
||||
holder.registerProblem(nameIdentifier1,
|
||||
InspectionsBundle.message("inspection.nullable.problems.annotated.field.setter.parameter.not.annotated",
|
||||
StringUtil.getShortName(anno)),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
addAnnoFix);
|
||||
}
|
||||
if (PropertyUtil.isSimpleSetter(setter)) {
|
||||
if (annotated.isDeclaredNotNull && manager.isNullable(parameter, false)) {
|
||||
final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
|
||||
assertValidElement(setter, parameter, nameIdentifier1);
|
||||
holder.registerProblem(nameIdentifier1, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.setter.parameter.conflict",
|
||||
StringUtil.getShortName(anno), nullableSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
addAnnoFix);
|
||||
}
|
||||
else if (annotated.isDeclaredNullable && manager.isNotNull(parameter, false)) {
|
||||
final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
|
||||
assertValidElement(setter, parameter, nameIdentifier1);
|
||||
holder.registerProblem(nameIdentifier1, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.setter.parameter.conflict", StringUtil.getShortName(anno), notNullSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
addAnnoFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiExpression rhs : DfaPsiUtil.findAllConstructorInitializers(field)) {
|
||||
if (rhs instanceof PsiReferenceExpression) {
|
||||
PsiElement target = ((PsiReferenceExpression)rhs).resolve();
|
||||
if (target instanceof PsiParameter) {
|
||||
PsiParameter parameter = (PsiParameter)target;
|
||||
AddAnnotationPsiFix fix = new AddAnnotationPsiFix(anno, parameter, PsiNameValuePair.EMPTY_ARRAY, ArrayUtil.toStringArray(annoToRemove));
|
||||
if (REPORT_NOT_ANNOTATED_GETTER && !AnnotationUtil.isAnnotated(parameter, manager.getAllAnnotations(), false, false) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) {
|
||||
final PsiIdentifier nameIdentifier2 = parameter.getNameIdentifier();
|
||||
assert nameIdentifier2 != null : parameter;
|
||||
holder.registerProblem(nameIdentifier2, InspectionsBundle
|
||||
.message("inspection.nullable.problems.annotated.field.constructor.parameter.not.annotated",
|
||||
StringUtil.getShortName(anno)),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fix);
|
||||
continue;
|
||||
}
|
||||
if (annotated.isDeclaredNotNull && manager.isNullable(parameter, false)) {
|
||||
final PsiIdentifier nameIdentifier2 = parameter.getNameIdentifier();
|
||||
assert nameIdentifier2 != null : parameter;
|
||||
holder.registerProblem(nameIdentifier2, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.constructor.parameter.conflict", StringUtil.getShortName(anno),
|
||||
nullableSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
fix);
|
||||
}
|
||||
else if (annotated.isDeclaredNullable && manager.isNotNull(parameter, false)) {
|
||||
boolean usedAsQualifier = !ReferencesSearch.search(parameter).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
return !(element instanceof PsiReferenceExpression && element.getParent() instanceof PsiReferenceExpression);
|
||||
}
|
||||
});
|
||||
if (!usedAsQualifier) {
|
||||
final PsiIdentifier nameIdentifier2 = parameter.getNameIdentifier();
|
||||
assert nameIdentifier2 != null : parameter;
|
||||
holder.registerProblem(nameIdentifier2, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.constructor.parameter.conflict", StringUtil.getShortName(anno),
|
||||
notNullSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
fix);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValidElement(PsiMethod setter, PsiParameter parameter, PsiIdentifier nameIdentifier1) {
|
||||
LOG.assertTrue(nameIdentifier1 != null && nameIdentifier1.isPhysical(), setter.getText());
|
||||
LOG.assertTrue(parameter.isPhysical(), setter.getText());
|
||||
}
|
||||
|
||||
@Override public void visitParameter(PsiParameter parameter) {
|
||||
if (!PsiUtil.isLanguageLevel5OrHigher(parameter)) return;
|
||||
check(parameter, holder, parameter.getType());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class Annotated {
|
||||
private final boolean isDeclaredNotNull;
|
||||
private final boolean isDeclaredNullable;
|
||||
|
||||
private Annotated(final boolean isDeclaredNotNull, final boolean isDeclaredNullable) {
|
||||
this.isDeclaredNotNull = isDeclaredNotNull;
|
||||
this.isDeclaredNullable = isDeclaredNullable;
|
||||
}
|
||||
}
|
||||
private static Annotated check(final PsiModifierListOwner parameter, final ProblemsHolder holder, PsiType type) {
|
||||
final NullableNotNullManager manager = NullableNotNullManager.getInstance(holder.getProject());
|
||||
PsiAnnotation isDeclaredNotNull = AnnotationUtil.findAnnotation(parameter, manager.getNotNulls());
|
||||
PsiAnnotation isDeclaredNullable = AnnotationUtil.findAnnotation(parameter, manager.getNullables());
|
||||
if (isDeclaredNullable != null && isDeclaredNotNull != null) {
|
||||
reportNullableNotNullConflict(holder, parameter, isDeclaredNullable, isDeclaredNotNull);
|
||||
}
|
||||
if ((isDeclaredNotNull != null || isDeclaredNullable != null) && type != null && TypeConversionUtil.isPrimitive(type.getCanonicalText())) {
|
||||
PsiAnnotation annotation = isDeclaredNotNull == null ? isDeclaredNullable : isDeclaredNotNull;
|
||||
reportPrimitiveType(holder, annotation, annotation, parameter);
|
||||
}
|
||||
return new Annotated(isDeclaredNotNull != null,isDeclaredNullable != null);
|
||||
}
|
||||
|
||||
private static void reportPrimitiveType(final ProblemsHolder holder, final PsiElement psiElement, final PsiAnnotation annotation,
|
||||
final PsiModifierListOwner listOwner) {
|
||||
holder.registerProblem(psiElement.isPhysical() ? psiElement : listOwner.getNavigationElement(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.primitive.type.annotation"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(annotation, listOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("inspection.nullable.problems.display.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return GroupNames.BUGS_GROUP_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getShortName() {
|
||||
return "NullableProblems";
|
||||
}
|
||||
|
||||
private void checkNullableStuffForMethod(PsiMethod method, final ProblemsHolder holder) {
|
||||
Annotated annotated = check(method, holder, method.getReturnType());
|
||||
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
|
||||
List<MethodSignatureBackedByPsiMethod> superMethodSignatures = method.findSuperMethodSignaturesIncludingStatic(true);
|
||||
boolean reported_not_annotated_method_overrides_notnull = false;
|
||||
boolean reported_nullable_method_overrides_notnull = false;
|
||||
boolean[] reported_notnull_parameter_overrides_nullable = new boolean[parameters.length];
|
||||
boolean[] reported_not_annotated_parameter_overrides_notnull = new boolean[parameters.length];
|
||||
|
||||
final NullableNotNullManager nullableManager = NullableNotNullManager.getInstance(holder.getProject());
|
||||
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
|
||||
PsiMethod superMethod = superMethodSignature.getMethod();
|
||||
if (!reported_nullable_method_overrides_notnull
|
||||
&& REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE
|
||||
&& annotated.isDeclaredNullable
|
||||
&& NullableNotNullManager.isNotNull(superMethod)) {
|
||||
reported_nullable_method_overrides_notnull = true;
|
||||
holder.registerProblem(method.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.Nullable.method.overrides.NotNull"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
if (!reported_not_annotated_method_overrides_notnull
|
||||
&& REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL
|
||||
&& !annotated.isDeclaredNullable
|
||||
&& !annotated.isDeclaredNotNull
|
||||
&& NullableNotNullManager.isNotNull(superMethod)) {
|
||||
reported_not_annotated_method_overrides_notnull = true;
|
||||
final String defaultNotNull = nullableManager.getDefaultNotNull();
|
||||
final String[] annotationsToRemove = ArrayUtil.toStringArray(nullableManager.getNullables());
|
||||
final LocalQuickFix fix = AnnotationUtil.isAnnotatingApplicable(method, defaultNotNull)
|
||||
? createAnnotateMethodFix(defaultNotNull, annotationsToRemove)
|
||||
: createChangeDefaultNotNullFix(nullableManager, superMethod);
|
||||
holder.registerProblem(method.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.method.overrides.NotNull"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(fix));
|
||||
}
|
||||
if (REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE || REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL) {
|
||||
PsiParameter[] superParameters = superMethod.getParameterList().getParameters();
|
||||
if (superParameters.length != parameters.length) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
PsiParameter superParameter = superParameters[i];
|
||||
if (!reported_notnull_parameter_overrides_nullable[i] && REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE &&
|
||||
nullableManager.isNotNull(parameter, false) &&
|
||||
nullableManager.isNullable(superParameter, false)) {
|
||||
reported_notnull_parameter_overrides_nullable[i] = true;
|
||||
holder.registerProblem(parameter.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.NotNull.parameter.overrides.Nullable"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
if (!reported_not_annotated_parameter_overrides_notnull[i] && REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL) {
|
||||
if (!AnnotationUtil.isAnnotated(parameter, nullableManager.getAllAnnotations(), false, false) &&
|
||||
nullableManager.isNotNull(superParameter, false)) {
|
||||
reported_not_annotated_parameter_overrides_notnull[i] = true;
|
||||
final LocalQuickFix fix = AnnotationUtil.isAnnotatingApplicable(parameter, nullableManager.getDefaultNotNull())
|
||||
? new AddNotNullAnnotationFix(parameter)
|
||||
: createChangeDefaultNotNullFix(nullableManager, superParameter);
|
||||
holder.registerProblem(parameter.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.parameter.overrides.NotNull"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(fix));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS) {
|
||||
boolean[] parameterAnnotated = new boolean[parameters.length];
|
||||
boolean[] parameterQuickFixSuggested = new boolean[parameters.length];
|
||||
boolean hasAnnotatedParameter = false;
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
parameterAnnotated[i] = nullableManager.isNotNull(parameter, false);
|
||||
hasAnnotatedParameter |= parameterAnnotated[i];
|
||||
}
|
||||
if (hasAnnotatedParameter || annotated.isDeclaredNotNull) {
|
||||
PsiManager manager = method.getManager();
|
||||
final String defaultNotNull = nullableManager.getDefaultNotNull();
|
||||
final boolean superMethodApplicable = AnnotationUtil.isAnnotatingApplicable(method, defaultNotNull);
|
||||
PsiMethod[] overridings =
|
||||
OverridingMethodsSearch.search(method, GlobalSearchScope.allScope(manager.getProject()), true).toArray(PsiMethod.EMPTY_ARRAY);
|
||||
boolean methodQuickFixSuggested = false;
|
||||
for (PsiMethod overriding : overridings) {
|
||||
if (!manager.isInProject(overriding)) continue;
|
||||
|
||||
final boolean applicable = AnnotationUtil.isAnnotatingApplicable(overriding, defaultNotNull);
|
||||
if (!methodQuickFixSuggested
|
||||
&& annotated.isDeclaredNotNull
|
||||
&& !nullableManager.isNotNull(overriding, false)
|
||||
&& (nullableManager.isNullable(overriding, false) || !nullableManager.isNullable(overriding, true))) {
|
||||
method.getNameIdentifier(); //load tree
|
||||
PsiAnnotation annotation = AnnotationUtil.findAnnotation(method, nullableManager.getNotNulls());
|
||||
final String[] annotationsToRemove = ArrayUtil.toStringArray(nullableManager.getNullables());
|
||||
|
||||
final LocalQuickFix fix;
|
||||
if (applicable) {
|
||||
fix = new MyAnnotateMethodFix(defaultNotNull, annotationsToRemove);
|
||||
}
|
||||
else {
|
||||
fix = superMethodApplicable ? null : createChangeDefaultNotNullFix(nullableManager, method);
|
||||
}
|
||||
|
||||
PsiElement psiElement = annotation;
|
||||
if (!annotation.isPhysical()) {
|
||||
psiElement = method.getNameIdentifier();
|
||||
if (psiElement == null) continue;
|
||||
}
|
||||
holder.registerProblem(psiElement, InspectionsBundle.message("nullable.stuff.problems.overridden.methods.are.not.annotated"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(fix));
|
||||
methodQuickFixSuggested = true;
|
||||
}
|
||||
if (hasAnnotatedParameter) {
|
||||
PsiParameter[] psiParameters = overriding.getParameterList().getParameters();
|
||||
for (int i = 0; i < psiParameters.length; i++) {
|
||||
if (parameterQuickFixSuggested[i]) continue;
|
||||
PsiParameter parameter = psiParameters[i];
|
||||
if (parameterAnnotated[i] && !nullableManager.isNotNull(parameter, false) && !nullableManager.isNullable(parameter, false)) {
|
||||
parameters[i].getNameIdentifier(); //be sure that corresponding tree element available
|
||||
PsiAnnotation annotation = AnnotationUtil.findAnnotation(parameters[i], nullableManager.getNotNulls());
|
||||
PsiElement psiElement = annotation;
|
||||
if (!annotation.isPhysical()) {
|
||||
psiElement = parameters[i].getNameIdentifier();
|
||||
if (psiElement == null) continue;
|
||||
}
|
||||
holder.registerProblem(psiElement,
|
||||
InspectionsBundle.message("nullable.stuff.problems.overridden.method.parameters.are.not.annotated"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(!applicable
|
||||
? createChangeDefaultNotNullFix(nullableManager, parameters[i])
|
||||
: new AnnotateOverriddenMethodParameterFix(defaultNotNull,
|
||||
nullableManager.getDefaultNullable())));
|
||||
parameterQuickFixSuggested[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static LocalQuickFix[] wrapFix(LocalQuickFix fix) {
|
||||
if (fix == null) return LocalQuickFix.EMPTY_ARRAY;
|
||||
return new LocalQuickFix[]{fix};
|
||||
}
|
||||
|
||||
private static LocalQuickFix createChangeDefaultNotNullFix(NullableNotNullManager nullableManager, PsiModifierListOwner modifierListOwner) {
|
||||
final PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierListOwner, nullableManager.getNotNulls());
|
||||
if (annotation != null) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
|
||||
if (referenceElement != null && referenceElement.resolve() != null) {
|
||||
return new ChangeNullableDefaultsFix(annotation.getQualifiedName(), null, nullableManager);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected AnnotateMethodFix createAnnotateMethodFix(final String defaultNotNull, final String[] annotationsToRemove) {
|
||||
return new AnnotateMethodFix(defaultNotNull, annotationsToRemove);
|
||||
}
|
||||
|
||||
private static void reportNullableNotNullConflict(final ProblemsHolder holder, final PsiModifierListOwner listOwner, final PsiAnnotation declaredNullable,
|
||||
final PsiAnnotation declaredNotNull) {
|
||||
holder.registerProblem(declaredNotNull.isPhysical() ? declaredNotNull : listOwner.getNavigationElement(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.Nullable.NotNull.conflict"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(declaredNotNull, listOwner));
|
||||
holder.registerProblem(declaredNullable.isPhysical() ? declaredNullable : listOwner.getNavigationElement(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.Nullable.NotNull.conflict"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(declaredNullable, listOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
throw new RuntimeException("No UI in headless mode");
|
||||
}
|
||||
|
||||
private static class MyAnnotateMethodFix extends AnnotateMethodFix {
|
||||
public MyAnnotateMethodFix(String defaultNotNull, String[] annotationsToRemove) {
|
||||
super(defaultNotNull, annotationsToRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean annotateOverriddenMethods() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int shouldAnnotateBaseMethod(PsiMethod method, PsiMethod superMethod, Project project) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("annotate.overridden.methods.as.notnull", ClassUtil.extractClassName(myAnnotation));
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.intellij.codeInspection.wrongPackageStatement;
|
||||
|
||||
import com.intellij.codeHighlighting.HighlightDisplayLevel;
|
||||
import com.intellij.codeInsight.daemon.JavaErrorMessages;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
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: 14-Nov-2005
|
||||
*/
|
||||
public class WrongPackageStatementInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
@Override
|
||||
@Nullable
|
||||
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
|
||||
// does not work in tests since CodeInsightTestCase copies file into temporary location
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) return null;
|
||||
if (file instanceof PsiJavaFile) {
|
||||
if (isInJsp(file)) return null;
|
||||
PsiJavaFile javaFile = (PsiJavaFile)file;
|
||||
|
||||
PsiDirectory directory = javaFile.getContainingDirectory();
|
||||
if (directory == null) return null;
|
||||
PsiPackage dirPackage = JavaDirectoryService.getInstance().getPackage(directory);
|
||||
if (dirPackage == null) return null;
|
||||
PsiPackageStatement packageStatement = javaFile.getPackageStatement();
|
||||
|
||||
// highlight the first class in the file only
|
||||
PsiClass[] classes = javaFile.getClasses();
|
||||
if (classes.length == 0 && packageStatement == null) return null;
|
||||
|
||||
String packageName = dirPackage.getQualifiedName();
|
||||
if (!Comparing.strEqual(packageName, "", true) && packageStatement == null) {
|
||||
String description = JavaErrorMessages.message("missing.package.statement", packageName);
|
||||
|
||||
return new ProblemDescriptor[]{manager.createProblemDescriptor(classes[0].getNameIdentifier(), description,
|
||||
new AdjustPackageNameFix(packageName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly)};
|
||||
}
|
||||
if (packageStatement != null) {
|
||||
final PsiJavaCodeReferenceElement packageReference = packageStatement.getPackageReference();
|
||||
PsiPackage classPackage = (PsiPackage)packageReference.resolve();
|
||||
List<LocalQuickFix> availableFixes = new ArrayList<LocalQuickFix>();
|
||||
if (classPackage == null || !Comparing.equal(dirPackage.getQualifiedName(), packageReference.getQualifiedName(), true)) {
|
||||
availableFixes.add(new AdjustPackageNameFix(packageName));
|
||||
String packName = classPackage != null ? classPackage.getQualifiedName() : packageReference.getQualifiedName();
|
||||
addMoveToPackageFix(file, packName, availableFixes);
|
||||
}
|
||||
if (!availableFixes.isEmpty()){
|
||||
String description = JavaErrorMessages.message("package.name.file.path.mismatch",
|
||||
packageReference.getQualifiedName(),
|
||||
dirPackage.getQualifiedName());
|
||||
LocalQuickFix[] fixes = availableFixes.toArray(new LocalQuickFix[availableFixes.size()]);
|
||||
ProblemDescriptor descriptor =
|
||||
manager.createProblemDescriptor(packageStatement.getPackageReference(), description, isOnTheFly,
|
||||
fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
return new ProblemDescriptor[]{descriptor};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isInJsp(PsiFile file) {
|
||||
return PsiUtilCore.getTemplateLanguageFile(file) instanceof ServerPageFile;
|
||||
}
|
||||
|
||||
protected void addMoveToPackageFix(PsiFile file, String packName, List<LocalQuickFix> availableFixes) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public HighlightDisplayLevel getDefaultLevel() {
|
||||
return HighlightDisplayLevel.ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("wrong.package.statement");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
@NonNls
|
||||
public String getShortName() {
|
||||
return "WrongPackageStatement";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.PsiDiamondTypeElementImpl;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -203,7 +202,7 @@ public class CodeInsightUtil {
|
||||
PsiElement[] children = scope.getChildren();
|
||||
for (PsiElement child : children) {
|
||||
if (child instanceof PsiExpression) {
|
||||
if (areExpressionsEquivalent(RefactoringUtil.unparenthesizeExpression((PsiExpression)child), expr)) {
|
||||
if (JavaPsiEquivalenceUtil.areExpressionsEquivalent(RefactoringUtil.unparenthesizeExpression((PsiExpression)child), expr)) {
|
||||
array.add((PsiExpression)child);
|
||||
continue;
|
||||
}
|
||||
@@ -233,30 +232,6 @@ public class CodeInsightUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean areExpressionsEquivalent(PsiExpression expr1, PsiExpression expr2) {
|
||||
return PsiEquivalenceUtil.areElementsEquivalent(expr1, expr2, new Comparator<PsiElement>() {
|
||||
@Override
|
||||
public int compare(PsiElement o1, PsiElement o2) {
|
||||
if (o1 instanceof PsiParameter && o2 instanceof PsiParameter && ((PsiParameter)o1).getDeclarationScope() instanceof PsiMethod) {
|
||||
return ((PsiParameter)o1).getName().compareTo(((PsiParameter)o2).getName());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}, new Comparator<PsiElement>() {
|
||||
@Override
|
||||
public int compare(PsiElement o1, PsiElement o2) {
|
||||
if (!o1.textMatches(o2)) return 1;
|
||||
|
||||
if (o1 instanceof PsiDiamondTypeElementImpl && o2 instanceof PsiDiamondTypeElementImpl) {
|
||||
final PsiDiamondType.DiamondInferenceResult thisInferenceResult = new PsiDiamondTypeImpl(o1.getManager(), (PsiTypeElement)o1).resolveInferredTypes();
|
||||
final PsiDiamondType.DiamondInferenceResult otherInferenceResult = new PsiDiamondTypeImpl(o2.getManager(), (PsiTypeElement)o2).resolveInferredTypes();
|
||||
return thisInferenceResult.equals(otherInferenceResult) ? 0 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}, null, false);
|
||||
}
|
||||
|
||||
public static Editor positionCursor(final Project project, PsiFile targetFile, PsiElement element) {
|
||||
TextRange range = element.getTextRange();
|
||||
int textOffset = range.getStartOffset();
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtil;
|
||||
import com.intellij.codeInsight.ExpectedTypeInfo;
|
||||
import com.intellij.codeInsight.JavaPsiEquivalenceUtil;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementWeigher;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
@@ -79,7 +79,7 @@ class RecursionWeigher extends LookupElementWeigher {
|
||||
if (myCallQualifier != null &&
|
||||
myPositionQualifier != null &&
|
||||
myCallQualifier != myPositionQualifier &&
|
||||
CodeInsightUtil.areExpressionsEquivalent(myCallQualifier, myPositionQualifier)) {
|
||||
JavaPsiEquivalenceUtil.areExpressionsEquivalent(myCallQualifier, myPositionQualifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ class RecursionWeigher extends LookupElementWeigher {
|
||||
return Result.normal;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Nullable
|
||||
private String getSetterPropertyName(@Nullable PsiMethod calledMethod) {
|
||||
if (PropertyUtil.isSimplePropertySetter(calledMethod)) {
|
||||
assert calledMethod != null;
|
||||
|
||||
+60
-60
@@ -21,8 +21,6 @@ import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -96,7 +94,7 @@ public class AccessStaticViaInstanceFix extends LocalQuickFixAndIntentionActionO
|
||||
final PsiExpression qualifierExpression = myExpression.getQualifierExpression();
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
if (qualifierExpression != null) {
|
||||
if (!checkSideEffects(project, containingClass, qualifierExpression, factory, myExpression)) return;
|
||||
if (!checkSideEffects(project, containingClass, qualifierExpression, factory, myExpression,editor)) return;
|
||||
PsiElement newQualifier = qualifierExpression.replace(factory.createReferenceExpression(containingClass));
|
||||
PsiElement qualifiedWithClassName = myExpression.copy();
|
||||
newQualifier.delete();
|
||||
@@ -110,69 +108,71 @@ public class AccessStaticViaInstanceFix extends LocalQuickFixAndIntentionActionO
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSideEffects(final Project project, PsiClass containingClass, final PsiExpression qualifierExpression,
|
||||
PsiElementFactory factory, final PsiElement myExpression) {
|
||||
private boolean checkSideEffects(final Project project,
|
||||
PsiClass containingClass,
|
||||
final PsiExpression qualifierExpression,
|
||||
PsiElementFactory factory,
|
||||
final PsiElement myExpression,
|
||||
Editor editor) {
|
||||
final List<PsiElement> sideEffects = new ArrayList<PsiElement>();
|
||||
boolean hasSideEffects = RemoveUnusedVariableUtil.checkSideEffects(qualifierExpression, null, sideEffects);
|
||||
if (hasSideEffects && !myOnTheFly) return false;
|
||||
if (hasSideEffects && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
final TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
final Editor editor = PlatformDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext());
|
||||
if (editor == null) {
|
||||
return false;
|
||||
}
|
||||
HighlightManager.getInstance(project).addOccurrenceHighlights(editor, PsiUtilCore.toPsiElementArray(sideEffects), attributes, true,
|
||||
null);
|
||||
try {
|
||||
hasSideEffects = PsiUtil.isStatement(factory.createStatementFromText(qualifierExpression.getText(), qualifierExpression));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
hasSideEffects = false;
|
||||
}
|
||||
final PsiReferenceExpression qualifiedWithClassName = (PsiReferenceExpression)myExpression.copy();
|
||||
qualifiedWithClassName.setQualifierExpression(factory.createReferenceExpression(containingClass));
|
||||
final boolean canCopeWithSideEffects = hasSideEffects;
|
||||
final SideEffectWarningDialog dialog =
|
||||
new SideEffectWarningDialog(project, false, null, sideEffects.get(0).getText(), PsiExpressionTrimRenderer.render(qualifierExpression),
|
||||
canCopeWithSideEffects){
|
||||
@Override
|
||||
protected String sideEffectsDescription() {
|
||||
if (canCopeWithSideEffects) {
|
||||
return "<html><body>" +
|
||||
" There are possible side effects found in expression '" +
|
||||
qualifierExpression.getText() +
|
||||
"'<br>" +
|
||||
" You can:<ul><li><b>Remove</b> class reference along with whole expressions involved, or</li>" +
|
||||
" <li><b>Transform</b> qualified expression into the statement on its own.<br>" +
|
||||
" That is,<br>" +
|
||||
" <table border=1><tr><td><code>" +
|
||||
myExpression.getText() +
|
||||
"</code></td></tr></table><br> becomes: <br>" +
|
||||
" <table border=1><tr><td><code>" +
|
||||
qualifierExpression.getText() +
|
||||
";<br>" +
|
||||
qualifiedWithClassName.getText() +
|
||||
" </code></td></tr></table></li>" +
|
||||
" </body></html>";
|
||||
} else {
|
||||
return "<html><body> There are possible side effects found in expression '" + qualifierExpression.getText() + "'<br>" +
|
||||
"You can:<ul><li><b>Remove</b> class reference along with whole expressions involved, or</li></body></html>";
|
||||
}
|
||||
if (!hasSideEffects || ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return true;
|
||||
}
|
||||
if (editor == null) {
|
||||
return false;
|
||||
}
|
||||
TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
HighlightManager.getInstance(project).addOccurrenceHighlights(editor, PsiUtilCore.toPsiElementArray(sideEffects), attributes, true, null);
|
||||
try {
|
||||
hasSideEffects = PsiUtil.isStatement(factory.createStatementFromText(qualifierExpression.getText(), qualifierExpression));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
hasSideEffects = false;
|
||||
}
|
||||
final PsiReferenceExpression qualifiedWithClassName = (PsiReferenceExpression)myExpression.copy();
|
||||
qualifiedWithClassName.setQualifierExpression(factory.createReferenceExpression(containingClass));
|
||||
final boolean canCopeWithSideEffects = hasSideEffects;
|
||||
final SideEffectWarningDialog dialog =
|
||||
new SideEffectWarningDialog(project, false, null, sideEffects.get(0).getText(), PsiExpressionTrimRenderer.render(qualifierExpression),
|
||||
canCopeWithSideEffects){
|
||||
@Override
|
||||
protected String sideEffectsDescription() {
|
||||
if (canCopeWithSideEffects) {
|
||||
return "<html><body>" +
|
||||
" There are possible side effects found in expression '" +
|
||||
qualifierExpression.getText() +
|
||||
"'<br>" +
|
||||
" You can:<ul><li><b>Remove</b> class reference along with whole expressions involved, or</li>" +
|
||||
" <li><b>Transform</b> qualified expression into the statement on its own.<br>" +
|
||||
" That is,<br>" +
|
||||
" <table border=1><tr><td><code>" +
|
||||
myExpression.getText() +
|
||||
"</code></td></tr></table><br> becomes: <br>" +
|
||||
" <table border=1><tr><td><code>" +
|
||||
qualifierExpression.getText() +
|
||||
";<br>" +
|
||||
qualifiedWithClassName.getText() +
|
||||
" </code></td></tr></table></li>" +
|
||||
" </body></html>";
|
||||
}
|
||||
};
|
||||
dialog.show();
|
||||
int res = dialog.getExitCode();
|
||||
if (res == RemoveUnusedVariableUtil.CANCEL) return false;
|
||||
try {
|
||||
if (res == RemoveUnusedVariableUtil.MAKE_STATEMENT) {
|
||||
final PsiStatement statementFromText = factory.createStatementFromText(qualifierExpression.getText() + ";", null);
|
||||
final PsiStatement statement = PsiTreeUtil.getParentOfType(myExpression, PsiStatement.class);
|
||||
statement.getParent().addBefore(statementFromText, statement);
|
||||
return "<html><body> There are possible side effects found in expression '" + qualifierExpression.getText() + "'<br>" +
|
||||
"You can:<ul><li><b>Remove</b> class reference along with whole expressions involved, or</li></body></html>";
|
||||
}
|
||||
};
|
||||
dialog.show();
|
||||
int res = dialog.getExitCode();
|
||||
if (res == RemoveUnusedVariableUtil.CANCEL) return false;
|
||||
try {
|
||||
if (res == RemoveUnusedVariableUtil.MAKE_STATEMENT) {
|
||||
final PsiStatement statementFromText = factory.createStatementFromText(qualifierExpression.getText() + ";", null);
|
||||
final PsiStatement statement = PsiTreeUtil.getParentOfType(myExpression, PsiStatement.class);
|
||||
statement.getParent().addBefore(statementFromText, statement);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public class SimplifyBooleanExpressionAction implements IntentionAction{
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return new SimplifyBooleanExpressionFix(null,null).getFamilyName();
|
||||
return SimplifyBooleanExpressionFix.FAMILY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -99,10 +99,4 @@ public class MoveToPackageFix implements LocalQuickFix {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class SurroundWithIfFix implements LocalQuickFix {
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
|
||||
PsiElement[] elements = {anchorStatement};
|
||||
PsiElement prev = PsiTreeUtil.skipSiblingsBackward(anchorStatement, PsiWhiteSpace.class);
|
||||
if (prev instanceof PsiComment && SuppressManager.getInstance().getSuppressedInspectionIdsIn(prev) != null) {
|
||||
if (prev instanceof PsiComment && JavaSuppressionUtil.getSuppressedInspectionIdsIn(prev) != null) {
|
||||
elements = new PsiElement[]{prev, anchorStatement};
|
||||
}
|
||||
try {
|
||||
|
||||
+7
-79
@@ -15,91 +15,19 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.accessStaticViaInstance;
|
||||
|
||||
import com.intellij.codeInsight.daemon.JavaErrorMessages;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.AccessStaticViaInstanceFix;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil;
|
||||
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import com.intellij.psi.JavaResolveResult;
|
||||
import com.intellij.psi.PsiReferenceExpression;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
* Date: 15-Nov-2005
|
||||
*/
|
||||
public class AccessStaticViaInstance extends BaseJavaBatchLocalInspectionTool {
|
||||
public static final String ACCESS_STATIC_VIA_INSTANCE = "AccessStaticViaInstance";
|
||||
|
||||
public class AccessStaticViaInstance extends AccessStaticViaInstanceBase {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("access.static.via.instance");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
@NonNls
|
||||
public String getShortName() {
|
||||
return ACCESS_STATIC_VIA_INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlternativeID() {
|
||||
return "static-access";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
checkAccessStaticMemberViaInstanceReference(expression, holder, isOnTheFly);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void checkAccessStaticMemberViaInstanceReference(PsiReferenceExpression expr, ProblemsHolder holder, boolean onTheFly) {
|
||||
JavaResolveResult result = expr.advancedResolve(false);
|
||||
PsiElement resolved = result.getElement();
|
||||
|
||||
if (!(resolved instanceof PsiMember)) return;
|
||||
PsiExpression qualifierExpression = expr.getQualifierExpression();
|
||||
if (qualifierExpression == null) return;
|
||||
|
||||
if (qualifierExpression instanceof PsiReferenceExpression) {
|
||||
final PsiElement qualifierResolved = ((PsiReferenceExpression)qualifierExpression).resolve();
|
||||
if (qualifierResolved instanceof PsiClass || qualifierResolved instanceof PsiPackage) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC)) return;
|
||||
|
||||
String description = JavaErrorMessages.message("static.member.accessed.via.instance.reference",
|
||||
JavaHighlightUtil.formatType(qualifierExpression.getType()),
|
||||
HighlightMessageUtil.getSymbolName(resolved, result.getSubstitutor()));
|
||||
if (!onTheFly) {
|
||||
if (RemoveUnusedVariableUtil.checkSideEffects(qualifierExpression, null, new ArrayList<PsiElement>())) {
|
||||
holder.registerProblem(expr, description);
|
||||
return;
|
||||
}
|
||||
}
|
||||
holder.registerProblem(expr, description, new AccessStaticViaInstanceFix(expr, result, onTheFly));
|
||||
protected AccessStaticViaInstanceFix createAccessStaticViaInstanceFix(PsiReferenceExpression expr,
|
||||
boolean onTheFly,
|
||||
JavaResolveResult result) {
|
||||
return new AccessStaticViaInstanceFix(expr, result, onTheFly);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ import java.util.List;
|
||||
*/
|
||||
public class ConditionCheckDialog extends DialogWrapper {
|
||||
private final Project myProject;
|
||||
private final @NotNull Splitter mainSplitter;
|
||||
private final @NotNull MethodsPanel myIsNullCheckMethodPanel;
|
||||
private final @NotNull MethodsPanel myIsNotNullCheckMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertIsNullMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertIsNotNullMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertTrueMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertFalseMethodPanel;
|
||||
@NotNull private final Splitter mainSplitter;
|
||||
@NotNull private final MethodsPanel myIsNullCheckMethodPanel;
|
||||
@NotNull private final MethodsPanel myIsNotNullCheckMethodPanel;
|
||||
@NotNull private final MethodsPanel myAssertIsNullMethodPanel;
|
||||
@NotNull private final MethodsPanel myAssertIsNotNullMethodPanel;
|
||||
@NotNull private final MethodsPanel myAssertTrueMethodPanel;
|
||||
@NotNull private final MethodsPanel myAssertFalseMethodPanel;
|
||||
|
||||
public ConditionCheckDialog(Project project, String mainDialogTitle) {
|
||||
super(project, true);
|
||||
@@ -140,12 +140,12 @@ public class ConditionCheckDialog extends DialogWrapper {
|
||||
* Is Null, Is Not Null, Assert True and Assert False Method Panel at the top of the main Dialog.
|
||||
*/
|
||||
class MethodsPanel {
|
||||
private final @NotNull JBList myList;
|
||||
private final @NotNull JPanel myPanel;
|
||||
private final @NotNull Project myProject;
|
||||
@NotNull private final JBList myList;
|
||||
@NotNull private final JPanel myPanel;
|
||||
@NotNull private final Project myProject;
|
||||
private Set<MethodsPanel> otherPanels;
|
||||
|
||||
public MethodsPanel(final List<ConditionChecker> checkers, final ConditionChecker.Type type, final @NotNull Project myProject) {
|
||||
public MethodsPanel(final List<ConditionChecker> checkers, final ConditionChecker.Type type, @NotNull final Project myProject) {
|
||||
this.myProject = myProject;
|
||||
myList = new JBList(new CollectionListModel<ConditionChecker>(checkers));
|
||||
myPanel = new JPanel(new BorderLayout());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -13,43 +13,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: max
|
||||
* Date: Dec 24, 2001
|
||||
* Time: 2:46:32 PM
|
||||
* To change template for new class use
|
||||
* Code Style | Class Templates options (Tools | IDE Options).
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.NullableNotNullDialog;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpressionFix;
|
||||
import com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.ex.BaseLocalInspectionTool;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.SurroundWithIfFix;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
@@ -57,497 +31,20 @@ import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
public class DataFlowInspection extends BaseLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DataFlowInspection");
|
||||
@NonNls private static final String SHORT_NAME = "ConstantConditions";
|
||||
public boolean SUGGEST_NULLABLE_ANNOTATIONS = false;
|
||||
public boolean DONT_REPORT_TRUE_ASSERT_STATEMENTS = false;
|
||||
|
||||
public class DataFlowInspection extends DataFlowInspectionBase {
|
||||
@Override
|
||||
protected void addSurroundWithIfFix(PsiExpression qualifier, List<LocalQuickFix> fixes) {
|
||||
if (SurroundWithIfFix.isAvailable(qualifier)) {
|
||||
fixes.add(new SurroundWithIfFix(qualifier));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new OptionsPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitField(PsiField field) {
|
||||
analyzeCodeBlock(field, holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethod(PsiMethod method) {
|
||||
analyzeCodeBlock(method.getBody(), holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassInitializer(PsiClassInitializer initializer) {
|
||||
analyzeCodeBlock(initializer.getBody(), holder);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void analyzeCodeBlock(@Nullable final PsiElement scope, ProblemsHolder holder) {
|
||||
if (scope == null) return;
|
||||
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(SUGGEST_NULLABLE_ANNOTATIONS);
|
||||
final StandardInstructionVisitor visitor = new DataFlowInstructionVisitor(dfaRunner);
|
||||
final RunnerResult rc = dfaRunner.analyzeMethod(scope, visitor);
|
||||
if (rc == RunnerResult.OK) {
|
||||
if (dfaRunner.problemsDetected(visitor)) {
|
||||
createDescription(dfaRunner, holder, visitor);
|
||||
}
|
||||
}
|
||||
else if (rc == RunnerResult.TOO_COMPLEX) {
|
||||
if (scope.getParent() instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod)scope.getParent();
|
||||
final PsiIdentifier name = method.getNameIdentifier();
|
||||
if (name != null) { // Might be null for synthetic methods like JSP page.
|
||||
holder.registerProblem(name, InspectionsBundle.message("dataflow.too.complex"), ProblemHighlightType.WEAK_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LocalQuickFix[] createNPEFixes(PsiExpression qualifier, PsiExpression expression) {
|
||||
if (qualifier == null || expression == null) return null;
|
||||
if (qualifier instanceof PsiMethodCallExpression) return null;
|
||||
if (qualifier instanceof PsiLiteralExpression && ((PsiLiteralExpression)qualifier).getValue() == null) return null;
|
||||
|
||||
try {
|
||||
final List<LocalQuickFix> fixes = new SmartList<LocalQuickFix>();
|
||||
|
||||
if (PsiUtil.getLanguageLevel(qualifier).isAtLeast(LanguageLevel.JDK_1_4)) {
|
||||
final Project project = qualifier.getProject();
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
final PsiBinaryExpression binary = (PsiBinaryExpression)elementFactory.createExpressionFromText("a != null", null);
|
||||
binary.getLOperand().replace(qualifier);
|
||||
fixes.add(new AddAssertStatementFix(binary));
|
||||
}
|
||||
|
||||
if (SurroundWithIfFix.isAvailable(qualifier)) {
|
||||
fixes.add(new SurroundWithIfFix(qualifier));
|
||||
}
|
||||
if (ReplaceWithTernaryOperatorFix.isAvailable(qualifier, expression)) {
|
||||
fixes.add(new ReplaceWithTernaryOperatorFix(qualifier));
|
||||
}
|
||||
return fixes.toArray(new LocalQuickFix[fixes.size()]);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void createDescription(StandardDataFlowRunner runner, ProblemsHolder holder, StandardInstructionVisitor visitor) {
|
||||
Pair<Set<Instruction>, Set<Instruction>> constConditions = runner.getConstConditionalExpressions();
|
||||
Set<Instruction> trueSet = constConditions.getFirst();
|
||||
Set<Instruction> falseSet = constConditions.getSecond();
|
||||
|
||||
ArrayList<Instruction> allProblems = new ArrayList<Instruction>();
|
||||
allProblems.addAll(trueSet);
|
||||
allProblems.addAll(falseSet);
|
||||
allProblems.addAll(runner.getNPEInstructions());
|
||||
allProblems.addAll(runner.getCCEInstructions());
|
||||
allProblems.addAll(StandardDataFlowRunner.getRedundantInstanceofs(runner, visitor));
|
||||
|
||||
Collections.sort(allProblems, new Comparator<Instruction>() {
|
||||
@Override
|
||||
public int compare(Instruction i1, Instruction i2) {
|
||||
return i1.getIndex() - i2.getIndex();
|
||||
}
|
||||
});
|
||||
|
||||
HashSet<PsiElement> reportedAnchors = new HashSet<PsiElement>();
|
||||
|
||||
for (Instruction instruction : allProblems) {
|
||||
if (instruction instanceof MethodCallInstruction) {
|
||||
reportCallMayProduceNpe(holder, (MethodCallInstruction)instruction);
|
||||
}
|
||||
else if (instruction instanceof FieldReferenceInstruction) {
|
||||
reportFieldAccessMayProduceNpe(holder, (FieldReferenceInstruction)instruction);
|
||||
}
|
||||
else if (instruction instanceof TypeCastInstruction) {
|
||||
reportCastMayFail(holder, (TypeCastInstruction)instruction);
|
||||
}
|
||||
else if (instruction instanceof BranchingInstruction) {
|
||||
handleBranchingInstruction(holder, visitor, trueSet, falseSet, reportedAnchors, (BranchingInstruction)instruction);
|
||||
}
|
||||
}
|
||||
|
||||
reportNullableArguments(runner, holder);
|
||||
reportNullableAssignments(runner, holder);
|
||||
reportUnboxedNullables(runner, holder);
|
||||
reportNullableReturns(runner, holder);
|
||||
reportNullableArgumentsPassedToNonAnnotated(runner, holder);
|
||||
}
|
||||
|
||||
private static void reportNullableArgumentsPassedToNonAnnotated(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
Set<PsiExpression> exprs = runner.getNullableArgumentsPassedToNonAnnotatedParam();
|
||||
for (PsiExpression expr : exprs) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? "Passing <code>null</code> argument to non annotated parameter"
|
||||
: "Argument <code>#ref</code> #loc might be null but passed to non annotated parameter";
|
||||
LocalQuickFix[] fixes = createNPEFixes(expr, expr);
|
||||
final PsiElement parent = expr.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final int idx = ArrayUtil.find(((PsiExpressionList)parent).getExpressions(), expr);
|
||||
if (idx > -1) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiCallExpression) {
|
||||
final PsiMethod psiMethod = ((PsiCallExpression)gParent).resolveMethod();
|
||||
if (psiMethod != null && psiMethod.getManager().isInProject(psiMethod) && AnnotationUtil.isAnnotatingApplicable(psiMethod)) {
|
||||
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
|
||||
if (idx < parameters.length) {
|
||||
final AddNullableAnnotationFix addNullableAnnotationFix = new AddNullableAnnotationFix(parameters[idx]);
|
||||
fixes = fixes == null ? new LocalQuickFix[]{addNullableAnnotationFix} : ArrayUtil.append(fixes, addNullableAnnotationFix);
|
||||
holder.registerProblem(expr, text, fixes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportCallMayProduceNpe(ProblemsHolder holder, MethodCallInstruction mcInstruction) {
|
||||
if (mcInstruction.getCallExpression() instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression callExpression = (PsiMethodCallExpression)mcInstruction.getCallExpression();
|
||||
LocalQuickFix[] fix = createNPEFixes(callExpression.getMethodExpression().getQualifierExpression(), callExpression);
|
||||
|
||||
holder.registerProblem(callExpression,
|
||||
InspectionsBundle.message("dataflow.message.npe.method.invocation"),
|
||||
fix);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportFieldAccessMayProduceNpe(ProblemsHolder holder, FieldReferenceInstruction frInstruction) {
|
||||
PsiElement elementToAssert = frInstruction.getElementToAssert();
|
||||
PsiExpression expression = frInstruction.getExpression();
|
||||
if (expression instanceof PsiArrayAccessExpression) {
|
||||
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression);
|
||||
holder.registerProblem(expression,
|
||||
InspectionsBundle.message("dataflow.message.npe.array.access"),
|
||||
fix);
|
||||
}
|
||||
else {
|
||||
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression);
|
||||
holder.registerProblem(elementToAssert,
|
||||
InspectionsBundle.message("dataflow.message.npe.field.access"),
|
||||
fix);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportCastMayFail(ProblemsHolder holder, TypeCastInstruction instruction) {
|
||||
PsiTypeCastExpression typeCast = instruction.getCastExpression();
|
||||
holder.registerProblem(typeCast.getCastType(),
|
||||
InspectionsBundle.message("dataflow.message.cce", typeCast.getOperand().getText()));
|
||||
}
|
||||
|
||||
private void handleBranchingInstruction(ProblemsHolder holder,
|
||||
StandardInstructionVisitor visitor,
|
||||
Set<Instruction> trueSet,
|
||||
Set<Instruction> falseSet, HashSet<PsiElement> reportedAnchors, BranchingInstruction instruction) {
|
||||
PsiElement psiAnchor = instruction.getPsiAnchor();
|
||||
boolean underBinary = isAtRHSOfBooleanAnd(psiAnchor);
|
||||
if (instruction instanceof InstanceofInstruction && visitor.isInstanceofRedundant((InstanceofInstruction)instruction)) {
|
||||
if (visitor.canBeNull((BinopInstruction)instruction)) {
|
||||
holder.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.redundant.instanceof"),
|
||||
new RedundantInstanceofFix());
|
||||
}
|
||||
else {
|
||||
final LocalQuickFix localQuickFix = createSimplifyBooleanExpressionFix(psiAnchor, true);
|
||||
holder.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message(underBinary ? "dataflow.message.constant.condition.when.reached" : "dataflow.message.constant.condition", Boolean.toString(true)),
|
||||
localQuickFix == null ? null : new LocalQuickFix[]{localQuickFix});
|
||||
}
|
||||
}
|
||||
else if (psiAnchor instanceof PsiSwitchLabelStatement) {
|
||||
if (falseSet.contains(instruction)) {
|
||||
holder.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.unreachable.switch.label"));
|
||||
}
|
||||
}
|
||||
else if (psiAnchor != null && !reportedAnchors.contains(psiAnchor) && !isCompileConstantInIfCondition(psiAnchor)) {
|
||||
boolean evaluatesToTrue = trueSet.contains(instruction);
|
||||
if (onTheLeftSideOfConditionalAssignemnt(psiAnchor)) {
|
||||
holder.registerProblem(
|
||||
psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
|
||||
createSimplifyToAssignmentFix()
|
||||
);
|
||||
}
|
||||
else if (!skipReportingConstantCondition(visitor, psiAnchor, evaluatesToTrue)) {
|
||||
final LocalQuickFix fix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue);
|
||||
String message = InspectionsBundle.message(underBinary ?
|
||||
"dataflow.message.constant.condition.when.reached" :
|
||||
"dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
|
||||
holder.registerProblem(psiAnchor, message, fix == null ? null : new LocalQuickFix[]{fix});
|
||||
}
|
||||
reportedAnchors.add(psiAnchor);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean skipReportingConstantCondition(StandardInstructionVisitor visitor, PsiElement psiAnchor, boolean evaluatesToTrue) {
|
||||
return DONT_REPORT_TRUE_ASSERT_STATEMENTS && isAssertionEffectively(psiAnchor, evaluatesToTrue) ||
|
||||
visitor.silenceConstantCondition(psiAnchor);
|
||||
}
|
||||
|
||||
private static void reportNullableArguments(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
Set<PsiExpression> exprs = runner.getNullableArguments();
|
||||
for (PsiExpression expr : exprs) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.passing.null.argument")
|
||||
: InspectionsBundle.message("dataflow.message.passing.nullable.argument");
|
||||
LocalQuickFix[] fixes = createNPEFixes(expr, expr);
|
||||
holder.registerProblem(expr, text, fixes);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportNullableAssignments(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
for (PsiExpression expr : runner.getNullableAssignments()) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.assigning.null")
|
||||
: InspectionsBundle.message("dataflow.message.assigning.nullable");
|
||||
holder.registerProblem(expr, text);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportUnboxedNullables(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
for (PsiExpression expr : runner.getUnboxedNullables()) {
|
||||
holder.registerProblem(expr, InspectionsBundle.message("dataflow.message.unboxing"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportNullableReturns(StandardDataFlowRunner runner, ProblemsHolder holder) {
|
||||
for (PsiReturnStatement statement : runner.getNullableReturns()) {
|
||||
final PsiExpression expr = statement.getReturnValue();
|
||||
if (runner.isInNotNullMethod()) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.return.null.from.notnull")
|
||||
: InspectionsBundle.message("dataflow.message.return.nullable.from.notnull");
|
||||
holder.registerProblem(expr, text);
|
||||
}
|
||||
else if (AnnotationUtil.isAnnotatingApplicable(statement)) {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
? InspectionsBundle.message("dataflow.message.return.null.from.notnullable")
|
||||
: InspectionsBundle.message("dataflow.message.return.nullable.from.notnullable");
|
||||
final NullableNotNullManager manager = NullableNotNullManager.getInstance(expr.getProject());
|
||||
holder.registerProblem(expr, text, new AnnotateMethodFix(manager.getDefaultNullable(), ArrayUtil.toStringArray(manager.getNotNulls())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAssertionEffectively(PsiElement psiAnchor, boolean evaluatesToTrue) {
|
||||
PsiElement parent = psiAnchor.getParent();
|
||||
if (parent instanceof PsiAssertStatement) {
|
||||
return evaluatesToTrue;
|
||||
}
|
||||
if (parent instanceof PsiIfStatement && psiAnchor == ((PsiIfStatement)parent).getCondition()) {
|
||||
PsiStatement thenBranch = ((PsiIfStatement)parent).getThenBranch();
|
||||
if (thenBranch instanceof PsiThrowStatement) {
|
||||
return !evaluatesToTrue;
|
||||
}
|
||||
if (thenBranch instanceof PsiBlockStatement) {
|
||||
PsiStatement[] statements = ((PsiBlockStatement)thenBranch).getCodeBlock().getStatements();
|
||||
if (statements.length == 1 && statements[0] instanceof PsiThrowStatement) {
|
||||
return !evaluatesToTrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isAtRHSOfBooleanAnd(PsiElement expr) {
|
||||
PsiElement cur = expr;
|
||||
|
||||
while (cur != null && !(cur instanceof PsiMember)) {
|
||||
PsiElement parent = cur.getParent();
|
||||
|
||||
if (parent instanceof PsiBinaryExpression && cur == ((PsiBinaryExpression)parent).getROperand()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cur = parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isCompileConstantInIfCondition(PsiElement element) {
|
||||
if (!(element instanceof PsiReferenceExpression)) return false;
|
||||
PsiElement resolved = ((PsiReferenceExpression)element).resolve();
|
||||
if (!(resolved instanceof PsiField)) return false;
|
||||
PsiField field = (PsiField)resolved;
|
||||
|
||||
if (!field.hasModifierProperty(PsiModifier.FINAL)) return false;
|
||||
if (!field.hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiPrefixExpression && ((PsiPrefixExpression)parent).getOperationTokenType() == JavaTokenType.EXCL) {
|
||||
element = parent;
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return parent instanceof PsiIfStatement && ((PsiIfStatement)parent).getCondition() == element;
|
||||
}
|
||||
|
||||
private static boolean isNullLiteralExpression(PsiExpression expr) {
|
||||
if (expr instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression literalExpression = (PsiLiteralExpression)expr;
|
||||
return PsiType.NULL.equals(literalExpression.getType());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean onTheLeftSideOfConditionalAssignemnt(final PsiElement psiAnchor) {
|
||||
final PsiElement parent = psiAnchor.getParent();
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiAssignmentExpression expression = (PsiAssignmentExpression)parent;
|
||||
if (expression.getLExpression() == psiAnchor) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LocalQuickFix createSimplifyBooleanExpressionFix(PsiElement element, final boolean value) {
|
||||
SimplifyBooleanExpressionFix fix = createIntention(element, value);
|
||||
if (fix == null) return null;
|
||||
final String text = fix.getText();
|
||||
return new LocalQuickFix() {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement psiElement = descriptor.getPsiElement();
|
||||
if (psiElement == null) return;
|
||||
final SimplifyBooleanExpressionFix fix = createIntention(psiElement, value);
|
||||
if (fix == null) return;
|
||||
try {
|
||||
LOG.assertTrue(psiElement.isValid());
|
||||
fix.invoke(project, null, psiElement.getContainingFile());
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.simplify.boolean.expression.quickfix");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LocalQuickFix createSimplifyToAssignmentFix() {
|
||||
return new LocalQuickFix() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.simplify.to.assignment.quickfix.name");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.simplify.boolean.expression.quickfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement psiElement = descriptor.getPsiElement();
|
||||
if (psiElement == null) return;
|
||||
|
||||
final PsiAssignmentExpression assignmentExpression = PsiTreeUtil.getParentOfType(psiElement, PsiAssignmentExpression.class);
|
||||
if (assignmentExpression == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
final String lExpressionText = assignmentExpression.getLExpression().getText();
|
||||
final PsiExpression rExpression = assignmentExpression.getRExpression();
|
||||
final String rExpressionText = rExpression != null ? rExpression.getText() : "";
|
||||
assignmentExpression.replace(factory.createExpressionFromText(lExpressionText + " = " + rExpressionText, psiElement));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static SimplifyBooleanExpressionFix createIntention(PsiElement element, boolean value) {
|
||||
if (!(element instanceof PsiExpression)) return null;
|
||||
final PsiExpression expression = (PsiExpression)element;
|
||||
while (element.getParent() instanceof PsiExpression) {
|
||||
element = element.getParent();
|
||||
}
|
||||
final SimplifyBooleanExpressionFix fix = new SimplifyBooleanExpressionFix(expression, value);
|
||||
// simplify intention already active
|
||||
if (!fix.isAvailable(element.getProject(), null, element.getContainingFile()) ||
|
||||
SimplifyBooleanExpressionFix.canBeSimplified((PsiExpression)element)) {
|
||||
return null;
|
||||
}
|
||||
return fix;
|
||||
}
|
||||
|
||||
private static class RedundantInstanceofFix implements LocalQuickFix {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.redundant.instanceof.quickfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(descriptor.getPsiElement())) return;
|
||||
final PsiElement psiElement = descriptor.getPsiElement();
|
||||
if (psiElement instanceof PsiInstanceOfExpression) {
|
||||
try {
|
||||
final PsiExpression compareToNull = JavaPsiFacade.getInstance(psiElement.getProject()).getElementFactory().
|
||||
createExpressionFromText(((PsiInstanceOfExpression)psiElement).getOperand().getText() + " != null", psiElement.getParent());
|
||||
psiElement.replace(compareToNull);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.display.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return GroupNames.BUGS_GROUP_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getShortName() {
|
||||
return SHORT_NAME;
|
||||
}
|
||||
|
||||
private class OptionsPanel extends JPanel {
|
||||
private final JCheckBox mySuggestNullables;
|
||||
private final JCheckBox myDontReportTrueAsserts;
|
||||
@@ -627,47 +124,4 @@ public class DataFlowInspection extends BaseLocalInspectionTool {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
private final StandardDataFlowRunner myRunner;
|
||||
|
||||
private DataFlowInstructionVisitor(StandardDataFlowRunner runner) {
|
||||
myRunner = runner;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAssigningToNotNullableVariable(AssignInstruction instruction) {
|
||||
myRunner.onAssigningToNotNullableVariable(instruction.getRExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNullableReturn(CheckReturnValueInstruction instruction) {
|
||||
myRunner.onNullableReturn(instruction.getReturn());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstructionProducesCCE(TypeCastInstruction instruction) {
|
||||
myRunner.onInstructionProducesCCE(instruction);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstructionProducesNPE(Instruction instruction) {
|
||||
if (instruction instanceof MethodCallInstruction &&
|
||||
((MethodCallInstruction)instruction).getMethodType() == MethodCallInstruction.MethodType.UNBOXING) {
|
||||
myRunner.onUnboxingNullable(((MethodCallInstruction)instruction).getContext());
|
||||
}
|
||||
else {
|
||||
myRunner.onInstructionProducesNPE(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPassingNullParameter(PsiExpression arg) {
|
||||
myRunner.onPassingNullParameter(arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPassingNullParameterToNonAnnotated(DataFlowRunner runner, PsiExpression arg) {
|
||||
myRunner.onPassingNullParameterToNonAnnotated(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -39,17 +39,17 @@ import static com.intellij.codeInsight.ConditionChecker.Type.*;
|
||||
* Dialog that appears when the user clicks the Add Button or double clicks a row item in a MethodsPanel. The MethodsPanel is accessed from the ConditionCheckDialog
|
||||
*/
|
||||
class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChangeListener, ItemListener {
|
||||
private final @NotNull ConditionChecker.Type myType;
|
||||
private final @NotNull Project myProject;
|
||||
private final @NotNull ParameterDropDown parameterDropDown;
|
||||
private final @NotNull MethodDropDown methodDropDown;
|
||||
private final @NotNull ClassField classField;
|
||||
private final @NotNull Set<ConditionChecker> myOtherCheckers;
|
||||
private final @Nullable ConditionChecker myPreviouslySelectedChecker;
|
||||
@NotNull private final ConditionChecker.Type myType;
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final ParameterDropDown parameterDropDown;
|
||||
@NotNull private final MethodDropDown methodDropDown;
|
||||
@NotNull private final ClassField classField;
|
||||
@NotNull private final Set<ConditionChecker> myOtherCheckers;
|
||||
@Nullable private final ConditionChecker myPreviouslySelectedChecker;
|
||||
/**
|
||||
* Set by the OK and/or Cancel actions so that the caller can retrieve it via a call to getMethodIsNullIsNotNullChecker
|
||||
*/
|
||||
private @Nullable ConditionChecker mySelectedChecker;
|
||||
@Nullable private ConditionChecker mySelectedChecker;
|
||||
|
||||
MethodCheckerDetailsDialog(@Nullable ConditionChecker previouslySelectedChecker,
|
||||
@NotNull ConditionChecker.Type type,
|
||||
@@ -263,8 +263,8 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange
|
||||
*/
|
||||
static class ClassField extends EditorTextFieldWithBrowseButton implements ActionListener, DocumentListener {
|
||||
public static final String PROPERTY_PSICLASS = "ClassField.myPsiClass";
|
||||
private final @NotNull Project myProject;
|
||||
private @Nullable PsiClass myPsiClass;
|
||||
@NotNull private final Project myProject;
|
||||
@Nullable private PsiClass myPsiClass;
|
||||
|
||||
public ClassField(@NotNull Project project, @Nullable PsiClass psiClass) {
|
||||
super(project, true, buildVisibilityChecker());
|
||||
@@ -341,9 +341,9 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange
|
||||
* Drop Down for picking Method Name
|
||||
*/
|
||||
static class MethodDropDown extends JComboBox implements PropertyChangeListener {
|
||||
private final @NotNull ConditionChecker.Type myType;
|
||||
private final @NotNull SortedComboBoxModel<MethodWrapper> myModel;
|
||||
private @Nullable PsiClass myPsiClass;
|
||||
@NotNull private final ConditionChecker.Type myType;
|
||||
@NotNull private final SortedComboBoxModel<MethodWrapper> myModel;
|
||||
@Nullable private PsiClass myPsiClass;
|
||||
|
||||
MethodDropDown(@Nullable PsiClass psiClass,
|
||||
@Nullable PsiMethod psiMethod,
|
||||
@@ -478,9 +478,9 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange
|
||||
* Drop Down for picking Parameter Name
|
||||
*/
|
||||
static class ParameterDropDown extends JComboBox implements PropertyChangeListener, ItemListener {
|
||||
private final @NotNull SortedComboBoxModel<ParameterWrapper> myModel;
|
||||
private final @NotNull ConditionChecker.Type myType;
|
||||
private @Nullable PsiMethod myPsiMethod;
|
||||
@NotNull private final SortedComboBoxModel<ParameterWrapper> myModel;
|
||||
@NotNull private final ConditionChecker.Type myType;
|
||||
@Nullable private PsiMethod myPsiMethod;
|
||||
|
||||
public ParameterDropDown(@Nullable PsiMethod psiMethod,
|
||||
@Nullable PsiParameter psiParameter,
|
||||
@@ -582,8 +582,8 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange
|
||||
}
|
||||
|
||||
class ParameterWrapper implements Comparable<ParameterWrapper> {
|
||||
private final @NotNull String id;
|
||||
private final @NotNull PsiParameter psiParameter;
|
||||
@NotNull private final String id;
|
||||
@NotNull private final PsiParameter psiParameter;
|
||||
private final int index;
|
||||
|
||||
ParameterWrapper(@NotNull PsiParameter psiParameter, int index) {
|
||||
@@ -624,8 +624,8 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange
|
||||
}
|
||||
|
||||
static class MethodWrapper implements Comparable<MethodWrapper> {
|
||||
private final @NotNull PsiMethod myPsiMethod;
|
||||
private final @NotNull String myId;
|
||||
@NotNull private final PsiMethod myPsiMethod;
|
||||
@NotNull private final String myId;
|
||||
|
||||
MethodWrapper(@NotNull PsiMethod psiMethod) {
|
||||
this.myPsiMethod = psiMethod;
|
||||
|
||||
+1
-489
@@ -15,473 +15,23 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.nullable;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.NullableNotNullDialog;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationFix;
|
||||
import com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix;
|
||||
import com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.ex.BaseLocalInspectionTool;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
public class NullableStuffInspection extends BaseLocalInspectionTool {
|
||||
// deprecated fields remain to minimize changes to users inspection profiles (which are often located in version control).
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL = true;
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL = true;
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE = true;
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL = true;
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_GETTER = true;
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_SETTER_PARAMETER = true;
|
||||
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = true; // remains for test
|
||||
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD = true;
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#" + NullableStuffInspection.class.getName());
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override public void visitMethod(PsiMethod method) {
|
||||
if (!PsiUtil.isLanguageLevel5OrHigher(method)) return;
|
||||
checkNullableStuffForMethod(method, holder);
|
||||
}
|
||||
|
||||
@Override public void visitField(PsiField field) {
|
||||
if (!PsiUtil.isLanguageLevel5OrHigher(field)) return;
|
||||
final PsiType type = field.getType();
|
||||
final Annotated annotated = check(field, holder, type);
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(type)) {
|
||||
return;
|
||||
}
|
||||
Project project = holder.getProject();
|
||||
final NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
|
||||
if (annotated.isDeclaredNotNull ^ annotated.isDeclaredNullable) {
|
||||
final String anno = annotated.isDeclaredNotNull ? manager.getDefaultNotNull() : manager.getDefaultNullable();
|
||||
final List<String> annoToRemove = annotated.isDeclaredNotNull ? manager.getNullables() : manager.getNotNulls();
|
||||
|
||||
if (!AnnotationUtil.isAnnotatingApplicable(field, anno)) {
|
||||
final PsiAnnotation notNull = AnnotationUtil.findAnnotation(field, manager.getNotNulls());
|
||||
final PsiAnnotation nullable = AnnotationUtil.findAnnotation(field, manager.getNullables());
|
||||
holder.registerProblem(field.getNameIdentifier(), "Nullable/NotNull defaults are not accessible in current context",
|
||||
new ChangeNullableDefaultsFix(notNull, nullable, manager));
|
||||
return;
|
||||
}
|
||||
|
||||
String propName = JavaCodeStyleManager.getInstance(project).variableNameToPropertyName(field.getName(), VariableKind.FIELD);
|
||||
final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
|
||||
final PsiMethod getter = PropertyUtil.findPropertyGetter(field.getContainingClass(), propName, isStatic, false);
|
||||
final String nullableSimpleName = StringUtil.getShortName(manager.getDefaultNullable());
|
||||
final String notNullSimpleName = StringUtil.getShortName(manager.getDefaultNotNull());
|
||||
final PsiIdentifier nameIdentifier = getter == null ? null : getter.getNameIdentifier();
|
||||
if (nameIdentifier != null && nameIdentifier.isPhysical()) {
|
||||
if (PropertyUtil.isSimpleGetter(getter)) {
|
||||
if (REPORT_NOT_ANNOTATED_GETTER) {
|
||||
if (!AnnotationUtil.isAnnotated(getter, manager.getAllAnnotations(), false, false) &&
|
||||
!TypeConversionUtil.isPrimitiveAndNotNull(getter.getReturnType())) {
|
||||
holder.registerProblem(nameIdentifier, InspectionsBundle
|
||||
.message("inspection.nullable.problems.annotated.field.getter.not.annotated", StringUtil.getShortName(anno)),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new AnnotateMethodFix(anno, ArrayUtil.toStringArray(annoToRemove)));
|
||||
}
|
||||
}
|
||||
if (annotated.isDeclaredNotNull && manager.isNullable(getter, false)) {
|
||||
holder.registerProblem(nameIdentifier, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.getter.conflict", StringUtil.getShortName(anno), nullableSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new AnnotateMethodFix(anno, ArrayUtil.toStringArray(annoToRemove)));
|
||||
} else if (annotated.isDeclaredNullable && manager.isNotNull(getter, false)) {
|
||||
holder.registerProblem(nameIdentifier, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.getter.conflict", StringUtil.getShortName(anno), notNullSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new AnnotateMethodFix(anno, ArrayUtil.toStringArray(annoToRemove)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final PsiClass containingClass = field.getContainingClass();
|
||||
final PsiMethod setter = PropertyUtil.findPropertySetter(containingClass, propName, isStatic, false);
|
||||
if (setter != null) {
|
||||
final PsiParameter[] parameters = setter.getParameterList().getParameters();
|
||||
assert parameters.length == 1 : setter.getText();
|
||||
final PsiParameter parameter = parameters[0];
|
||||
LOG.assertTrue(parameter != null, setter.getText());
|
||||
if (REPORT_NOT_ANNOTATED_GETTER && !AnnotationUtil.isAnnotated(parameter, manager.getAllAnnotations(), false, false) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) {
|
||||
final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
|
||||
assertValidElement(setter, parameter, nameIdentifier1);
|
||||
holder.registerProblem(nameIdentifier1,
|
||||
InspectionsBundle.message("inspection.nullable.problems.annotated.field.setter.parameter.not.annotated",
|
||||
StringUtil.getShortName(anno)),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
new AddAnnotationFix(anno, parameter, ArrayUtil.toStringArray(annoToRemove)));
|
||||
}
|
||||
if (PropertyUtil.isSimpleSetter(setter)) {
|
||||
if (annotated.isDeclaredNotNull && manager.isNullable(parameter, false)) {
|
||||
final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
|
||||
assertValidElement(setter, parameter, nameIdentifier1);
|
||||
holder.registerProblem(nameIdentifier1, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.setter.parameter.conflict",
|
||||
StringUtil.getShortName(anno), nullableSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
new AddAnnotationFix(anno, parameter, ArrayUtil.toStringArray(annoToRemove)));
|
||||
}
|
||||
else if (annotated.isDeclaredNullable && manager.isNotNull(parameter, false)) {
|
||||
final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
|
||||
assertValidElement(setter, parameter, nameIdentifier1);
|
||||
holder.registerProblem(nameIdentifier1, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.setter.parameter.conflict", StringUtil.getShortName(anno), notNullSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
new AddAnnotationFix(anno, parameter, ArrayUtil.toStringArray(annoToRemove)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiExpression rhs : findAllConstructorInitializers(field)) {
|
||||
if (rhs instanceof PsiReferenceExpression) {
|
||||
PsiElement target = ((PsiReferenceExpression)rhs).resolve();
|
||||
if (target instanceof PsiParameter) {
|
||||
PsiParameter parameter = (PsiParameter)target;
|
||||
if (REPORT_NOT_ANNOTATED_GETTER && !AnnotationUtil.isAnnotated(parameter, manager.getAllAnnotations(), false, false) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) {
|
||||
final PsiIdentifier nameIdentifier2 = parameter.getNameIdentifier();
|
||||
assert nameIdentifier2 != null : parameter;
|
||||
holder.registerProblem(nameIdentifier2, InspectionsBundle
|
||||
.message("inspection.nullable.problems.annotated.field.constructor.parameter.not.annotated",
|
||||
StringUtil.getShortName(anno)),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new AddAnnotationFix(anno, parameter, ArrayUtil.toStringArray(annoToRemove)));
|
||||
continue;
|
||||
}
|
||||
if (annotated.isDeclaredNotNull && manager.isNullable(parameter, false)) {
|
||||
final PsiIdentifier nameIdentifier2 = parameter.getNameIdentifier();
|
||||
assert nameIdentifier2 != null : parameter;
|
||||
holder.registerProblem(nameIdentifier2, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.constructor.parameter.conflict", StringUtil.getShortName(anno),
|
||||
nullableSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
new AddAnnotationFix(anno, parameter, ArrayUtil.toStringArray(annoToRemove)));
|
||||
}
|
||||
else if (annotated.isDeclaredNullable && manager.isNotNull(parameter, false)) {
|
||||
boolean usedAsQualifier = !ReferencesSearch.search(parameter).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression && element.getParent() instanceof PsiReferenceExpression) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!usedAsQualifier) {
|
||||
final PsiIdentifier nameIdentifier2 = parameter.getNameIdentifier();
|
||||
assert nameIdentifier2 != null : parameter;
|
||||
holder.registerProblem(nameIdentifier2, InspectionsBundle.message(
|
||||
"inspection.nullable.problems.annotated.field.constructor.parameter.conflict", StringUtil.getShortName(anno),
|
||||
notNullSimpleName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
new AddAnnotationFix(anno, parameter, ArrayUtil.toStringArray(annoToRemove)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValidElement(PsiMethod setter, PsiParameter parameter, PsiIdentifier nameIdentifier1) {
|
||||
LOG.assertTrue(nameIdentifier1 != null && nameIdentifier1.isPhysical(), setter.getText());
|
||||
LOG.assertTrue(parameter.isPhysical(), setter.getText());
|
||||
}
|
||||
|
||||
@Override public void visitParameter(PsiParameter parameter) {
|
||||
if (!PsiUtil.isLanguageLevel5OrHigher(parameter)) return;
|
||||
check(parameter, holder, parameter.getType());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class Annotated {
|
||||
private final boolean isDeclaredNotNull;
|
||||
private final boolean isDeclaredNullable;
|
||||
|
||||
private Annotated(final boolean isDeclaredNotNull, final boolean isDeclaredNullable) {
|
||||
this.isDeclaredNotNull = isDeclaredNotNull;
|
||||
this.isDeclaredNullable = isDeclaredNullable;
|
||||
}
|
||||
}
|
||||
private static Annotated check(final PsiModifierListOwner parameter, final ProblemsHolder holder, PsiType type) {
|
||||
final NullableNotNullManager manager = NullableNotNullManager.getInstance(holder.getProject());
|
||||
PsiAnnotation isDeclaredNotNull = AnnotationUtil.findAnnotation(parameter, manager.getNotNulls());
|
||||
PsiAnnotation isDeclaredNullable = AnnotationUtil.findAnnotation(parameter, manager.getNullables());
|
||||
if (isDeclaredNullable != null && isDeclaredNotNull != null) {
|
||||
reportNullableNotNullConflict(holder, parameter, isDeclaredNullable, isDeclaredNotNull);
|
||||
}
|
||||
if ((isDeclaredNotNull != null || isDeclaredNullable != null) && type != null && TypeConversionUtil.isPrimitive(type.getCanonicalText())) {
|
||||
PsiAnnotation annotation = isDeclaredNotNull == null ? isDeclaredNullable : isDeclaredNotNull;
|
||||
reportPrimitiveType(holder, annotation, annotation, parameter);
|
||||
}
|
||||
return new Annotated(isDeclaredNotNull != null,isDeclaredNullable != null);
|
||||
}
|
||||
|
||||
private static void reportPrimitiveType(final ProblemsHolder holder, final PsiElement psiElement, final PsiAnnotation annotation,
|
||||
final PsiModifierListOwner listOwner) {
|
||||
holder.registerProblem(psiElement.isPhysical() ? psiElement : listOwner.getNavigationElement(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.primitive.type.annotation"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(annotation, listOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("inspection.nullable.problems.display.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return GroupNames.BUGS_GROUP_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getShortName() {
|
||||
return "NullableProblems";
|
||||
}
|
||||
|
||||
private void checkNullableStuffForMethod(PsiMethod method, final ProblemsHolder holder) {
|
||||
Annotated annotated = check(method, holder, method.getReturnType());
|
||||
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
|
||||
List<MethodSignatureBackedByPsiMethod> superMethodSignatures = method.findSuperMethodSignaturesIncludingStatic(true);
|
||||
boolean reported_not_annotated_method_overrides_notnull = false;
|
||||
boolean reported_nullable_method_overrides_notnull = false;
|
||||
boolean[] reported_notnull_parameter_overrides_nullable = new boolean[parameters.length];
|
||||
boolean[] reported_not_annotated_parameter_overrides_notnull = new boolean[parameters.length];
|
||||
|
||||
final NullableNotNullManager nullableManager = NullableNotNullManager.getInstance(holder.getProject());
|
||||
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
|
||||
PsiMethod superMethod = superMethodSignature.getMethod();
|
||||
if (!reported_nullable_method_overrides_notnull
|
||||
&& REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE
|
||||
&& annotated.isDeclaredNullable
|
||||
&& NullableNotNullManager.isNotNull(superMethod)) {
|
||||
reported_nullable_method_overrides_notnull = true;
|
||||
holder.registerProblem(method.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.Nullable.method.overrides.NotNull"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
if (!reported_not_annotated_method_overrides_notnull
|
||||
&& REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL
|
||||
&& !annotated.isDeclaredNullable
|
||||
&& !annotated.isDeclaredNotNull
|
||||
&& NullableNotNullManager.isNotNull(superMethod)) {
|
||||
reported_not_annotated_method_overrides_notnull = true;
|
||||
final String defaultNotNull = nullableManager.getDefaultNotNull();
|
||||
final String[] annotationsToRemove = ArrayUtil.toStringArray(nullableManager.getNullables());
|
||||
final LocalQuickFix fix = AnnotationUtil.isAnnotatingApplicable(method, defaultNotNull)
|
||||
? createAnnotateMethodFix(defaultNotNull, annotationsToRemove)
|
||||
: createChangeDefaultNotNullFix(nullableManager, superMethod);
|
||||
holder.registerProblem(method.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.method.overrides.NotNull"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(fix));
|
||||
}
|
||||
if (REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE || REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL) {
|
||||
PsiParameter[] superParameters = superMethod.getParameterList().getParameters();
|
||||
if (superParameters.length != parameters.length) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
PsiParameter superParameter = superParameters[i];
|
||||
if (!reported_notnull_parameter_overrides_nullable[i] && REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE &&
|
||||
nullableManager.isNotNull(parameter, false) &&
|
||||
nullableManager.isNullable(superParameter, false)) {
|
||||
reported_notnull_parameter_overrides_nullable[i] = true;
|
||||
holder.registerProblem(parameter.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.NotNull.parameter.overrides.Nullable"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
if (!reported_not_annotated_parameter_overrides_notnull[i] && REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL) {
|
||||
if (!AnnotationUtil.isAnnotated(parameter, nullableManager.getAllAnnotations(), false, false) &&
|
||||
nullableManager.isNotNull(superParameter, false)) {
|
||||
reported_not_annotated_parameter_overrides_notnull[i] = true;
|
||||
final LocalQuickFix fix = AnnotationUtil.isAnnotatingApplicable(parameter, nullableManager.getDefaultNotNull())
|
||||
? new AddNotNullAnnotationFix(parameter)
|
||||
: createChangeDefaultNotNullFix(nullableManager, superParameter);
|
||||
holder.registerProblem(parameter.getNameIdentifier(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.parameter.overrides.NotNull"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(fix));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS) {
|
||||
boolean[] parameterAnnotated = new boolean[parameters.length];
|
||||
boolean[] parameterQuickFixSuggested = new boolean[parameters.length];
|
||||
boolean hasAnnotatedParameter = false;
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
parameterAnnotated[i] = nullableManager.isNotNull(parameter, false);
|
||||
hasAnnotatedParameter |= parameterAnnotated[i];
|
||||
}
|
||||
if (hasAnnotatedParameter || annotated.isDeclaredNotNull) {
|
||||
PsiManager manager = method.getManager();
|
||||
final String defaultNotNull = nullableManager.getDefaultNotNull();
|
||||
final boolean superMethodApplicable = AnnotationUtil.isAnnotatingApplicable(method, defaultNotNull);
|
||||
PsiMethod[] overridings =
|
||||
OverridingMethodsSearch.search(method, GlobalSearchScope.allScope(manager.getProject()), true).toArray(PsiMethod.EMPTY_ARRAY);
|
||||
boolean methodQuickFixSuggested = false;
|
||||
for (PsiMethod overriding : overridings) {
|
||||
if (!manager.isInProject(overriding)) continue;
|
||||
|
||||
final boolean applicable = AnnotationUtil.isAnnotatingApplicable(overriding, defaultNotNull);
|
||||
if (!methodQuickFixSuggested
|
||||
&& annotated.isDeclaredNotNull
|
||||
&& !nullableManager.isNotNull(overriding, false)
|
||||
&& (nullableManager.isNullable(overriding, false) || !nullableManager.isNullable(overriding, true))) {
|
||||
method.getNameIdentifier(); //load tree
|
||||
PsiAnnotation annotation = AnnotationUtil.findAnnotation(method, nullableManager.getNotNulls());
|
||||
final String[] annotationsToRemove = ArrayUtil.toStringArray(nullableManager.getNullables());
|
||||
|
||||
final LocalQuickFix fix;
|
||||
if (applicable) {
|
||||
fix = new MyAnnotateMethodFix(defaultNotNull, annotationsToRemove);
|
||||
}
|
||||
else {
|
||||
fix = superMethodApplicable ? null : createChangeDefaultNotNullFix(nullableManager, method);
|
||||
}
|
||||
|
||||
PsiElement psiElement = annotation;
|
||||
if (!annotation.isPhysical()) {
|
||||
psiElement = method.getNameIdentifier();
|
||||
if (psiElement == null) continue;
|
||||
}
|
||||
holder.registerProblem(psiElement, InspectionsBundle.message("nullable.stuff.problems.overridden.methods.are.not.annotated"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(fix));
|
||||
methodQuickFixSuggested = true;
|
||||
}
|
||||
if (hasAnnotatedParameter) {
|
||||
PsiParameter[] psiParameters = overriding.getParameterList().getParameters();
|
||||
for (int i = 0; i < psiParameters.length; i++) {
|
||||
if (parameterQuickFixSuggested[i]) continue;
|
||||
PsiParameter parameter = psiParameters[i];
|
||||
if (parameterAnnotated[i] && !nullableManager.isNotNull(parameter, false) && !nullableManager.isNullable(parameter, false)) {
|
||||
parameters[i].getNameIdentifier(); //be sure that corresponding tree element available
|
||||
PsiAnnotation annotation = AnnotationUtil.findAnnotation(parameters[i], nullableManager.getNotNulls());
|
||||
PsiElement psiElement = annotation;
|
||||
if (!annotation.isPhysical()) {
|
||||
psiElement = parameters[i].getNameIdentifier();
|
||||
if (psiElement == null) continue;
|
||||
}
|
||||
holder.registerProblem(psiElement,
|
||||
InspectionsBundle.message("nullable.stuff.problems.overridden.method.parameters.are.not.annotated"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
wrapFix(!applicable
|
||||
? createChangeDefaultNotNullFix(nullableManager, parameters[i])
|
||||
: new AnnotateOverriddenMethodParameterFix(defaultNotNull,
|
||||
nullableManager.getDefaultNullable())));
|
||||
parameterQuickFixSuggested[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static LocalQuickFix[] wrapFix(LocalQuickFix fix) {
|
||||
if (fix == null) return LocalQuickFix.EMPTY_ARRAY;
|
||||
return new LocalQuickFix[]{fix};
|
||||
}
|
||||
|
||||
private static LocalQuickFix createChangeDefaultNotNullFix(NullableNotNullManager nullableManager, PsiModifierListOwner modifierListOwner) {
|
||||
final PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierListOwner, nullableManager.getNotNulls());
|
||||
if (annotation != null) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
|
||||
if (referenceElement != null && referenceElement.resolve() != null) {
|
||||
return new ChangeNullableDefaultsFix(annotation.getQualifiedName(), null, nullableManager);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected AnnotateMethodFix createAnnotateMethodFix(final String defaultNotNull, final String[] annotationsToRemove) {
|
||||
return new AnnotateMethodFix(defaultNotNull, annotationsToRemove);
|
||||
}
|
||||
|
||||
private static void reportNullableNotNullConflict(final ProblemsHolder holder, final PsiModifierListOwner listOwner, final PsiAnnotation declaredNullable,
|
||||
final PsiAnnotation declaredNotNull) {
|
||||
holder.registerProblem(declaredNotNull.isPhysical() ? declaredNotNull : listOwner.getNavigationElement(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.Nullable.NotNull.conflict"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(declaredNotNull, listOwner));
|
||||
holder.registerProblem(declaredNullable.isPhysical() ? declaredNullable : listOwner.getNavigationElement(),
|
||||
InspectionsBundle.message("inspection.nullable.problems.Nullable.NotNull.conflict"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(declaredNullable, listOwner));
|
||||
}
|
||||
|
||||
public class NullableStuffInspection extends NullableStuffInspectionBase {
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new OptionsPanel();
|
||||
}
|
||||
|
||||
private static class MyAddNullableAnnotationFix extends AddNullableAnnotationFix {
|
||||
public MyAddNullableAnnotationFix(PsiParameter parameter) {
|
||||
super(parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyAnnotateMethodFix extends AnnotateMethodFix {
|
||||
public MyAnnotateMethodFix(String defaultNotNull, String[] annotationsToRemove) {
|
||||
super(defaultNotNull, annotationsToRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean annotateOverriddenMethods() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("annotate.overridden.methods.as.notnull", ClassUtil.extractClassName(myAnnotation));
|
||||
}
|
||||
}
|
||||
|
||||
private class OptionsPanel extends JPanel {
|
||||
private JCheckBox myNNParameterOverridesN;
|
||||
private JCheckBox myNAMethodOverridesNN;
|
||||
@@ -527,42 +77,4 @@ public class NullableStuffInspection extends BaseLocalInspectionTool {
|
||||
REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<PsiExpression> findAllConstructorInitializers(PsiField field) {
|
||||
final List<PsiExpression> result = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
ContainerUtil.addIfNotNull(result, field.getInitializer());
|
||||
|
||||
PsiClass containingClass = field.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
LocalSearchScope scope = new LocalSearchScope(containingClass.getConstructors());
|
||||
ReferencesSearch.search(field, scope, false).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
final PsiAssignmentExpression assignment = getAssignmentExpressionIfOnAssignmentLhs(element);
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(assignment, PsiMethod.class);
|
||||
if (method != null && method.isConstructor() && assignment != null) {
|
||||
ContainerUtil.addIfNotNull(result, assignment.getRExpression());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiAssignmentExpression getAssignmentExpressionIfOnAssignmentLhs(PsiElement expression) {
|
||||
PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, PsiParenthesizedExpression.class);
|
||||
if (!(parent instanceof PsiAssignmentExpression)) {
|
||||
return null;
|
||||
}
|
||||
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
|
||||
if (!PsiTreeUtil.isAncestor(assignmentExpression.getLExpression(), expression, false)) {
|
||||
return null;
|
||||
}
|
||||
return assignmentExpression;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-91
@@ -15,105 +15,22 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.wrongPackageStatement;
|
||||
|
||||
import com.intellij.codeHighlighting.HighlightDisplayLevel;
|
||||
import com.intellij.codeInsight.daemon.JavaErrorMessages;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.MoveToPackageFix;
|
||||
import com.intellij.psi.PsiFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
* Date: 14-Nov-2005
|
||||
*/
|
||||
public class WrongPackageStatementInspection extends BaseJavaLocalInspectionTool {
|
||||
public class WrongPackageStatementInspection extends WrongPackageStatementInspectionBase {
|
||||
@Override
|
||||
@Nullable
|
||||
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
|
||||
// does not work in tests since CodeInsightTestCase copies file into temporary location
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) return null;
|
||||
if (file instanceof PsiJavaFile) {
|
||||
if (JspPsiUtil.isInJspFile(file)) return null;
|
||||
PsiJavaFile javaFile = (PsiJavaFile)file;
|
||||
|
||||
PsiDirectory directory = javaFile.getContainingDirectory();
|
||||
if (directory == null) return null;
|
||||
PsiPackage dirPackage = JavaDirectoryService.getInstance().getPackage(directory);
|
||||
if (dirPackage == null) return null;
|
||||
PsiPackageStatement packageStatement = javaFile.getPackageStatement();
|
||||
|
||||
// highlight the first class in the file only
|
||||
PsiClass[] classes = javaFile.getClasses();
|
||||
if (classes.length == 0 && packageStatement == null) return null;
|
||||
|
||||
String packageName = dirPackage.getQualifiedName();
|
||||
if (!Comparing.strEqual(packageName, "", true) && packageStatement == null) {
|
||||
String description = JavaErrorMessages.message("missing.package.statement", packageName);
|
||||
|
||||
return new ProblemDescriptor[]{manager.createProblemDescriptor(classes[0].getNameIdentifier(), description,
|
||||
new AdjustPackageNameFix(packageName),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly)};
|
||||
}
|
||||
if (packageStatement != null) {
|
||||
final PsiJavaCodeReferenceElement packageReference = packageStatement.getPackageReference();
|
||||
PsiPackage classPackage = (PsiPackage)packageReference.resolve();
|
||||
List<LocalQuickFix> availableFixes = new ArrayList<LocalQuickFix>();
|
||||
if (classPackage == null || !Comparing.equal(dirPackage.getQualifiedName(), packageReference.getQualifiedName(), true)) {
|
||||
availableFixes.add(new AdjustPackageNameFix(packageName));
|
||||
MoveToPackageFix moveToPackageFix = new MoveToPackageFix(classPackage != null ? classPackage.getQualifiedName() : packageReference.getQualifiedName());
|
||||
if (moveToPackageFix.isAvailable(file)) {
|
||||
availableFixes.add(moveToPackageFix);
|
||||
}
|
||||
}
|
||||
if (!availableFixes.isEmpty()){
|
||||
String description = JavaErrorMessages.message("package.name.file.path.mismatch",
|
||||
packageReference.getQualifiedName(),
|
||||
dirPackage.getQualifiedName());
|
||||
LocalQuickFix[] fixes = availableFixes.toArray(new LocalQuickFix[availableFixes.size()]);
|
||||
ProblemDescriptor descriptor =
|
||||
manager.createProblemDescriptor(packageStatement.getPackageReference(), description, isOnTheFly,
|
||||
fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
return new ProblemDescriptor[]{descriptor};
|
||||
|
||||
}
|
||||
}
|
||||
protected void addMoveToPackageFix(PsiFile file, String packName, List<LocalQuickFix> availableFixes) {
|
||||
MoveToPackageFix moveToPackageFix = new MoveToPackageFix(packName);
|
||||
if (moveToPackageFix.isAvailable(file)) {
|
||||
availableFixes.add(moveToPackageFix);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public HighlightDisplayLevel getDefaultLevel() {
|
||||
return HighlightDisplayLevel.ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("wrong.package.statement");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
@NonNls
|
||||
public String getShortName() {
|
||||
return "WrongPackageStatement";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
*/
|
||||
package com.intellij.ide.util;
|
||||
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -27,7 +30,6 @@ import com.intellij.psi.presentation.java.SymbolPresentationUtil;
|
||||
import com.intellij.psi.search.PsiElementProcessor;
|
||||
import com.intellij.psi.search.searches.DeepestSuperMethodsSearch;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -68,7 +70,7 @@ public class SuperMethodWarningUtil {
|
||||
}
|
||||
|
||||
SuperMethodWarningDialog dialog =
|
||||
new SuperMethodWarningDialog(method.getProject(), UsageViewUtil.getDescriptiveName(method), actionString, superAbstract,
|
||||
new SuperMethodWarningDialog(method.getProject(), DescriptiveNameUtil.getDescriptiveName(method), actionString, superAbstract,
|
||||
parentInterface, aClass.isInterface(), ArrayUtil.toStringArray(superClasses));
|
||||
dialog.show();
|
||||
|
||||
@@ -97,7 +99,7 @@ public class SuperMethodWarningUtil {
|
||||
SuperMethodWarningDialog dialog =
|
||||
new SuperMethodWarningDialog(
|
||||
method.getProject(),
|
||||
UsageViewUtil.getDescriptiveName(method), actionString, containingClass.isInterface() || superMethod.hasModifierProperty(PsiModifier.ABSTRACT),
|
||||
DescriptiveNameUtil.getDescriptiveName(method), actionString, containingClass.isInterface() || superMethod.hasModifierProperty(PsiModifier.ABSTRACT),
|
||||
containingClass.isInterface(), aClass.isInterface(), containingClass.getQualifiedName()
|
||||
);
|
||||
dialog.show();
|
||||
@@ -154,4 +156,16 @@ public class SuperMethodWarningUtil {
|
||||
}
|
||||
}).createPopup().showInBestPositionFor(editor);
|
||||
}
|
||||
|
||||
public static int askWhetherShouldAnnotateBaseMethod(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
|
||||
String implement = !method.hasModifierProperty(PsiModifier.ABSTRACT) && superMethod.hasModifierProperty(PsiModifier.ABSTRACT)
|
||||
? InspectionsBundle.message("inspection.annotate.quickfix.implements")
|
||||
: InspectionsBundle.message("inspection.annotate.quickfix.overrides");
|
||||
String message = InspectionsBundle.message("inspection.annotate.quickfix.overridden.method.messages",
|
||||
DescriptiveNameUtil.getDescriptiveName(method), implement,
|
||||
DescriptiveNameUtil.getDescriptiveName(superMethod));
|
||||
String title = InspectionsBundle.message("inspection.annotate.quickfix.overridden.method.warning");
|
||||
return Messages.showYesNoCancelDialog(method.getProject(), message, title, Messages.getQuestionIcon());
|
||||
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.refactoring.changeClassSignature;
|
||||
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
@@ -27,7 +28,6 @@ import com.intellij.refactoring.ui.StringTableCellEditor;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.table.JBTable;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.EditableModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -116,7 +116,7 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
|
||||
}
|
||||
|
||||
protected JComponent createNorthPanel() {
|
||||
return new JLabel(RefactoringBundle.message("changeClassSignature.class.label.text", UsageViewUtil.getDescriptiveName(myClass)));
|
||||
return new JLabel(RefactoringBundle.message("changeClassSignature.class.label.text", DescriptiveNameUtil.getDescriptiveName(myClass)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.refactoring.changeSignature;
|
||||
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
@@ -25,7 +26,6 @@ import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CanonicalTypes;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.VisibilityUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -288,7 +288,7 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
|
||||
temporallyRevertChanges(method, oldText);
|
||||
doRefactor(processor);
|
||||
}
|
||||
}, RefactoringBundle.message("changing.signature.of.0", UsageViewUtil.getDescriptiveName(currentMethod)), null);
|
||||
}, RefactoringBundle.message("changing.signature.of.0", DescriptiveNameUtil.getDescriptiveName(currentMethod)), null);
|
||||
}
|
||||
|
||||
private void doRefactor(BaseRefactoringProcessor processor) {
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.intellij.refactoring.encapsulateFields;
|
||||
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -75,7 +76,7 @@ public class EncapsulateFieldsProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
|
||||
protected String getCommandName() {
|
||||
return RefactoringBundle.message("encapsulate.fields.command.name", UsageViewUtil.getDescriptiveName(myClass));
|
||||
return RefactoringBundle.message("encapsulate.fields.command.name", DescriptiveNameUtil.getDescriptiveName(myClass));
|
||||
}
|
||||
|
||||
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
|
||||
|
||||
+2
-2
@@ -17,6 +17,7 @@ package com.intellij.refactoring.extractInterface;
|
||||
|
||||
import com.intellij.history.LocalHistory;
|
||||
import com.intellij.history.LocalHistoryAction;
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
@@ -34,7 +35,6 @@ import com.intellij.refactoring.memberPullUp.PullUpHelper;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.DocCommentPolicy;
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
@@ -150,7 +150,7 @@ public class ExtractInterfaceHandler implements RefactoringActionHandler, Elemen
|
||||
}
|
||||
|
||||
private String getCommandName() {
|
||||
return RefactoringBundle.message("extract.interface.command.name", myInterfaceName, UsageViewUtil.getDescriptiveName(myClass));
|
||||
return RefactoringBundle.message("extract.interface.command.name", myInterfaceName, DescriptiveNameUtil.getDescriptiveName(myClass));
|
||||
}
|
||||
|
||||
public boolean isEnabledOnElements(PsiElement[] elements) {
|
||||
|
||||
+1
-1
@@ -1054,7 +1054,7 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
final PsiClass nullableAnnotationClass =
|
||||
JavaPsiFacade.getInstance(myProject).findClass(manager.getDefaultNullable(), GlobalSearchScope.allScope(myProject));
|
||||
if (nullableAnnotationClass != null) {
|
||||
new AddNullableAnnotationFix(newMethod).invoke(myProject, myEditor, myTargetClass.getContainingFile());
|
||||
new AddNullableAnnotationFix(newMethod).invoke(myProject, myTargetClass.getContainingFile(), newMethod, newMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,6 +22,7 @@ package com.intellij.refactoring.extractSuperclass;
|
||||
|
||||
import com.intellij.history.LocalHistory;
|
||||
import com.intellij.history.LocalHistoryAction;
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -41,7 +42,6 @@ import com.intellij.refactoring.memberPullUp.PullUpConflictsUtil;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.DocCommentPolicy;
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
@@ -182,7 +182,7 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra
|
||||
}
|
||||
|
||||
private String getCommandName(final PsiClass subclass, String newName) {
|
||||
return RefactoringBundle.message("extract.superclass.command.name", newName, UsageViewUtil.getDescriptiveName(subclass));
|
||||
return RefactoringBundle.message("extract.superclass.command.name", newName, DescriptiveNameUtil.getDescriptiveName(subclass));
|
||||
}
|
||||
|
||||
public boolean isEnabledOnElements(PsiElement[] elements) {
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.VisibilityUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -320,7 +321,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
|
||||
super.performRefactoring(usageInfos);
|
||||
if (myNewVisibility == null) return;
|
||||
for (PsiMember member : members) {
|
||||
VisibilityUtil.fixVisibility(usageInfos, member, myNewVisibility);
|
||||
VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usageInfos), member, myNewVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
|
||||
import com.intellij.codeInsight.generation.GenerateMembersUtil;
|
||||
import com.intellij.codeInsight.generation.OverrideImplementUtil;
|
||||
import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter;
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -44,7 +45,6 @@ import com.intellij.refactoring.util.classRefs.ClassReferenceScanner;
|
||||
import com.intellij.refactoring.util.classRefs.ClassReferenceSearchingScanner;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.usages.UsageInfoToUsageConverter;
|
||||
import com.intellij.usages.UsageTarget;
|
||||
import com.intellij.usages.UsageViewManager;
|
||||
@@ -906,7 +906,7 @@ public class InheritanceToDelegationProcessor extends BaseRefactoringProcessor {
|
||||
|
||||
|
||||
protected String getCommandName() {
|
||||
return RefactoringBundle.message("replace.inheritance.with.delegation.command", UsageViewUtil.getDescriptiveName(myClass));
|
||||
return RefactoringBundle.message("replace.inheritance.with.delegation.command", DescriptiveNameUtil.getDescriptiveName(myClass));
|
||||
}
|
||||
|
||||
private Set<PsiMember> getAllBaseClassMembers() {
|
||||
|
||||
+4
-4
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.refactoring.inline;
|
||||
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -30,7 +31,6 @@ import com.intellij.refactoring.rename.NonCodeUsageInfoFactory;
|
||||
import com.intellij.refactoring.util.*;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -179,8 +179,8 @@ public class InlineConstantFieldProcessor extends BaseRefactoringProcessor {
|
||||
} else if (initializer1 instanceof PsiMethodCallExpression) {
|
||||
referenceExpression = ((PsiMethodCallExpression)initializer1).getMethodExpression();
|
||||
}
|
||||
if (referenceExpression != null &&
|
||||
referenceExpression.getQualifierExpression() == null &&
|
||||
if (referenceExpression != null &&
|
||||
referenceExpression.getQualifierExpression() == null &&
|
||||
!(referenceExpression.advancedResolve(false).getCurrentFileResolveScope() instanceof PsiImportStaticStatement)) {
|
||||
referenceExpression.setQualifierExpression(qExpression);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ public class InlineConstantFieldProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
|
||||
protected String getCommandName() {
|
||||
return RefactoringBundle.message("inline.field.command", UsageViewUtil.getDescriptiveName(myField));
|
||||
return RefactoringBundle.message("inline.field.command", DescriptiveNameUtil.getDescriptiveName(myField));
|
||||
}
|
||||
|
||||
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.history.LocalHistory;
|
||||
import com.intellij.history.LocalHistoryAction;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.lang.refactoring.InlineHandler;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -51,7 +52,6 @@ import com.intellij.refactoring.rename.RenameJavaVariableProcessor;
|
||||
import com.intellij.refactoring.util.*;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
@@ -110,7 +110,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
myFactory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
|
||||
myCodeStyleManager = CodeStyleManager.getInstance(myProject);
|
||||
myJavaCodeStyle = JavaCodeStyleManager.getInstance(myProject);
|
||||
myDescriptiveName = UsageViewUtil.getDescriptiveName(myMethod);
|
||||
myDescriptiveName = DescriptiveNameUtil.getDescriptiveName(myMethod);
|
||||
}
|
||||
|
||||
protected String getCommandName() {
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@
|
||||
package com.intellij.refactoring.introduceParameter;
|
||||
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -535,7 +536,7 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
|
||||
}
|
||||
|
||||
protected String getCommandName() {
|
||||
return RefactoringBundle.message("introduce.parameter.command", UsageViewUtil.getDescriptiveName(myMethodToReplaceIn));
|
||||
return RefactoringBundle.message("introduce.parameter.command", DescriptiveNameUtil.getDescriptiveName(myMethodToReplaceIn));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+3
-2
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.refactoring.util.FixableUsageInfo;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.VisibilityUtil;
|
||||
|
||||
@@ -46,9 +47,9 @@ public class BeanClassVisibilityUsageInfo extends FixableUsageInfo {
|
||||
|
||||
@Override
|
||||
public void fixUsage() throws IncorrectOperationException {
|
||||
VisibilityUtil.fixVisibility(usages, existingClass, myNewVisibility);
|
||||
VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usages), existingClass, myNewVisibility);
|
||||
if (myExistingClassCompatibleConstructor != null) {
|
||||
VisibilityUtil.fixVisibility(usages, myExistingClassCompatibleConstructor, myNewVisibility);
|
||||
VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usages), myExistingClassCompatibleConstructor, myNewVisibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.refactoring.invertBoolean;
|
||||
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.help.HelpManager;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
@@ -47,7 +48,7 @@ public class InvertBooleanDialog extends RefactoringDialog {
|
||||
myLabel.setText(RefactoringBundle.message("invert.boolean.name.of.inverted.element", typeString));
|
||||
myCaptionLabel.setText(RefactoringBundle.message("invert.0.1",
|
||||
typeString,
|
||||
UsageViewUtil.getDescriptiveName(myElement)));
|
||||
DescriptiveNameUtil.getDescriptiveName(myElement)));
|
||||
|
||||
setTitle(InvertBooleanHandler.REFACTORING_NAME);
|
||||
init();
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
package com.intellij.refactoring.makeStatic;
|
||||
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -274,7 +275,7 @@ public abstract class MakeMethodOrClassStaticProcessor<T extends PsiTypeParamete
|
||||
}
|
||||
|
||||
protected String getCommandName() {
|
||||
return RefactoringBundle.message("make.static.command", UsageViewUtil.getDescriptiveName(myMember));
|
||||
return RefactoringBundle.message("make.static.command", DescriptiveNameUtil.getDescriptiveName(myMember));
|
||||
}
|
||||
|
||||
public T getMember() {
|
||||
|
||||
+3
-2
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
package com.intellij.refactoring.makeStatic;
|
||||
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.help.HelpManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
@@ -34,7 +35,6 @@ import com.intellij.refactoring.HelpID;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.ParameterTablePanel;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -222,7 +222,8 @@ public class MakeParameterizedStaticDialog extends AbstractMakeStaticDialog {
|
||||
if (isMakeClassParameter()) {
|
||||
final PsiMethod methodWithParameter = checkParameterDoesNotExist();
|
||||
if (methodWithParameter != null) {
|
||||
String who = methodWithParameter == myMember ? RefactoringBundle.message("this.method") : UsageViewUtil.getDescriptiveName(methodWithParameter);
|
||||
String who = methodWithParameter == myMember ? RefactoringBundle.message("this.method") : DescriptiveNameUtil
|
||||
.getDescriptiveName(methodWithParameter);
|
||||
String message = RefactoringBundle.message("0.already.has.parameter.named.1.use.this.name.anyway", who, getClassParameterName());
|
||||
ret = Messages.showYesNoDialog(myProject, message, RefactoringBundle.message("warning.title"), Messages.getWarningIcon());
|
||||
myClassParameterNameInputField.requestFocusInWindow();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user