diff options
Diffstat (limited to 'java/java-impl/src/com/intellij/codeInspection')
18 files changed, 290 insertions, 146 deletions
diff --git a/java/java-impl/src/com/intellij/codeInspection/LambdaCanBeMethReferenceInspection.java b/java/java-impl/src/com/intellij/codeInspection/LambdaCanBeMethReferenceInspection.java index 75116480fdf0..1c54bff2716e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/LambdaCanBeMethReferenceInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/LambdaCanBeMethReferenceInspection.java @@ -205,34 +205,42 @@ public class LambdaCanBeMethReferenceInspection extends BaseJavaLocalInspectionT final String methodReferenceName = methodExpression.getReferenceName(); if (qualifierExpression != null) { boolean isReceiverType = PsiMethodReferenceUtil.isReceiverType(functionalInterfaceType, containingClass, psiMethod); - methodRefText = (isReceiverType ? containingClass.getQualifiedName() : qualifierExpression.getText()) + "::" + ((PsiMethodCallExpression)element).getTypeArgumentList().getText() + methodReferenceName; + methodRefText = (isReceiverType ? getClassReferenceName(containingClass) : qualifierExpression.getText()) + "::" + ((PsiMethodCallExpression)element).getTypeArgumentList().getText() + methodReferenceName; } else { methodRefText = - (psiMethod.hasModifierProperty(PsiModifier.STATIC) ? containingClass.getQualifiedName() : "this") + "::" + methodReferenceName; + (psiMethod.hasModifierProperty(PsiModifier.STATIC) ? getClassReferenceName(containingClass) : "this") + "::" + methodReferenceName; } } else if (element instanceof PsiNewExpression) { final PsiMethod constructor = ((PsiNewExpression)element).resolveConstructor(); + PsiClass containingClass = null; if (constructor != null) { - final PsiClass containingClass = constructor.getContainingClass(); + containingClass = constructor.getContainingClass(); LOG.assertTrue(containingClass != null); - methodRefText = containingClass.getQualifiedName() + "::new"; } else { final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)element).getClassOrAnonymousClassReference(); if (classReference != null) { final JavaResolveResult resolve = classReference.advancedResolve(false); - final PsiElement containingClass = resolve.getElement(); - if (containingClass instanceof PsiClass) { - methodRefText = ((PsiClass)containingClass).getQualifiedName() + "::new"; + final PsiElement resolveElement = resolve.getElement(); + if (resolveElement instanceof PsiClass) { + containingClass = (PsiClass)resolveElement; } } } + if (containingClass != null) { + methodRefText = getClassReferenceName(containingClass) + "::new"; + } } return methodRefText; } + private static String getClassReferenceName(PsiClass containingClass) { + final String qualifiedName = containingClass.getQualifiedName(); + return qualifiedName != null ? qualifiedName : containingClass.getName(); + } + private static class ReplaceWithMethodRefFix implements LocalQuickFix { @NotNull @Override @@ -251,12 +259,21 @@ public class LambdaCanBeMethReferenceInspection extends BaseJavaLocalInspectionT final PsiElement element = descriptor.getPsiElement(); final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class); if (lambdaExpression == null) return; - final String methodRefText = createMethodReferenceText(element, lambdaExpression.getFunctionalInterfaceType()); + final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType(); + final String methodRefText = createMethodReferenceText(element, functionalInterfaceType); if (methodRefText != null) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); final PsiExpression psiExpression = - JavaPsiFacade.getElementFactory(project).createExpressionFromText(methodRefText, lambdaExpression); - JavaCodeStyleManager.getInstance(project).shortenClassReferences(lambdaExpression.replace(psiExpression)); + factory.createExpressionFromText(methodRefText, lambdaExpression); + PsiElement replace = lambdaExpression.replace(psiExpression); + if (((PsiMethodReferenceExpression)replace).getFunctionalInterfaceType() == null) { //ambiguity + final PsiTypeCastExpression cast = (PsiTypeCastExpression)factory.createExpressionFromText("(A)a", replace); + cast.getCastType().replace(factory.createTypeElement(functionalInterfaceType)); + cast.getOperand().replace(replace); + replace = replace.replace(cast); + } + JavaCodeStyleManager.getInstance(project).shortenClassReferences(replace); } } } diff --git a/java/java-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java b/java/java-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java index 3ee49d07d7b9..ee5e883e3a2d 100644 --- a/java/java-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java @@ -107,10 +107,12 @@ public class RedundantSuppressInspection extends GlobalInspectionTool{ @Nullable private CommonProblemDescriptor[] checkElement(RefClass refEntity, InspectionManager manager, final Project project) { - return checkElement(refEntity.getElement(), manager, project); + final PsiClass psiClass = refEntity.getElement(); + if (psiClass == null) return null; + return checkElement(psiClass, manager, project); } - public CommonProblemDescriptor[] checkElement(final PsiElement psiElement, InspectionManager manager, Project project) { + public CommonProblemDescriptor[] checkElement(@NotNull final PsiElement psiElement, InspectionManager manager, Project project) { final Map<PsiElement, Collection<String>> suppressedScopes = new THashMap<PsiElement, Collection<String>>(); psiElement.accept(new JavaRecursiveElementWalkingVisitor() { @Override public void visitModifierList(PsiModifierList list) { diff --git a/java/java-impl/src/com/intellij/codeInspection/ReplaceWithTernaryOperatorFix.java b/java/java-impl/src/com/intellij/codeInspection/ReplaceWithTernaryOperatorFix.java index d08ba72cc81f..cf08dacf7045 100644 --- a/java/java-impl/src/com/intellij/codeInspection/ReplaceWithTernaryOperatorFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/ReplaceWithTernaryOperatorFix.java @@ -49,7 +49,15 @@ public class ReplaceWithTernaryOperatorFix implements LocalQuickFix { @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final PsiElement element = descriptor.getPsiElement(); + PsiElement element = descriptor.getPsiElement(); + while (true) { + PsiElement parent = element.getParent(); + if (parent instanceof PsiReferenceExpression || parent instanceof PsiMethodCallExpression) { + element = parent; + } else { + break; + } + } if (!(element instanceof PsiExpression)) { return; } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 992969dda54e..e3170217680f 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -21,7 +21,9 @@ import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; @@ -31,7 +33,14 @@ import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.intellij.psi.CommonClassNames.JAVA_LANG_ERROR; +import static com.intellij.psi.CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION; +import static com.intellij.psi.CommonClassNames.JAVA_LANG_THROWABLE; class ControlFlowAnalyzer extends JavaElementVisitor { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer"); @@ -45,7 +54,8 @@ class ControlFlowAnalyzer extends JavaElementVisitor { private int myPassNumber; private Set<DfaVariableValue> myFields; private Stack<CatchDescriptor> myCatchStack; - private PsiType myRuntimeException; + private DfaValue myRuntimeException; + private DfaValue myError; ControlFlowAnalyzer(final DfaValueFactory valueFactory) { myFactory = valueFactory; @@ -54,7 +64,10 @@ class ControlFlowAnalyzer extends JavaElementVisitor { public ControlFlow buildControlFlow(PsiElement codeFragment) { if (codeFragment == null) return null; - myRuntimeException = PsiType.getJavaLangRuntimeException(codeFragment.getManager(), codeFragment.getResolveScope()); + PsiManager manager = codeFragment.getManager(); + GlobalSearchScope scope = codeFragment.getResolveScope(); + myRuntimeException = myFactory.getNotNullFactory().create(PsiType.getJavaLangRuntimeException(manager, scope)); + myError = myFactory.getNotNullFactory().create(PsiType.getJavaLangError(manager, scope)); myFields = new HashSet<DfaVariableValue>(); myCatchStack = new Stack<CatchDescriptor>(); myPassNumber = 1; @@ -638,10 +651,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor { continue; } - PsiType type = cd.getType(); - if (type instanceof PsiDisjunctionType) { - type = ((PsiDisjunctionType)type).getLeastUpperBound(); - } + PsiType type = cd.getLubType(); if (type instanceof PsiClassType && ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)type)) { addConditionalRuntimeThrow(cd, true); break; @@ -655,8 +665,22 @@ class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(branch); addInstruction(new EmptyStackInstruction()); if (forCatch) { - addInstruction(new PushInstruction(myFactory.getNotNullFactory().create(myRuntimeException), null)); - addGotoCatch(cd); + PsiType type = cd.getLubType(); + boolean isRuntime = InheritanceUtil.isInheritor(type, JAVA_LANG_RUNTIME_EXCEPTION) || ExceptionUtil.isGeneralExceptionType(type); + boolean isError = InheritanceUtil.isInheritor(type, JAVA_LANG_ERROR) || type.equalsToText(JAVA_LANG_THROWABLE); + if (isRuntime != isError) { + addInstruction(new PushInstruction(isRuntime ? myRuntimeException : myError, null)); + addGotoCatch(cd); + } else { + pushUnknown(); + final ConditionalGotoInstruction branch2 = new ConditionalGotoInstruction(-1, false, null); + addInstruction(branch2); + addInstruction(new PushInstruction(myError, null)); + addGotoCatch(cd); + branch2.setOffset(myCurrentFlow.getInstructionCount()); + addInstruction(new PushInstruction(myRuntimeException, null)); + addGotoCatch(cd); + } } else { addInstruction(new GosubInstruction(cd.getJumpOffset(this))); @@ -755,6 +779,14 @@ class ControlFlowAnalyzer extends JavaElementVisitor { return myType; } + PsiType getLubType() { + PsiType type = myType; + if (type instanceof PsiDisjunctionType) { + return ((PsiDisjunctionType)type).getLeastUpperBound(); + } + return type; + } + public boolean isFinally() { return myIsFinally; } @@ -1222,7 +1254,11 @@ class ControlFlowAnalyzer extends JavaElementVisitor { } } - addInstruction(new MethodCallInstruction(expression, createChainedVariableValue(expression))); + MethodCallInstruction callInstruction = new MethodCallInstruction(expression, createChainedVariableValue(expression)); + if (!DfaValueFactory.isEffectivelyUnqualified(methodExpression)) { + callInstruction.setShouldFlushFields(false); + } + addInstruction(callInstruction); if (!myCatchStack.isEmpty()) { addMethodThrows(expression.resolveMethod()); @@ -1560,7 +1596,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor { return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), false, null, isCall); } - if (DfaVariableState.isFinalField(var)) { + if (DfaUtil.isFinalField(var) || DfaUtil.isPlainMutableField(var)) { DfaVariableValue qualifierValue = createChainedVariableValue(qualifier); if (qualifierValue != null) { return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), false, qualifierValue, isCall || qualifierValue.isViaMethods()); @@ -1576,7 +1612,10 @@ class ControlFlowAnalyzer extends JavaElementVisitor { return (PsiVariable)target; } if (target instanceof PsiMethod) { - return PropertyUtils.getSimplyReturnedField((PsiMethod)target, PropertyUtils.getSingleReturnValue((PsiMethod)target)); + PsiMethod method = (PsiMethod)target; + if (PropertyUtils.isSimpleGetter(method)) { + return PropertyUtils.getSimplyReturnedField(method, PropertyUtils.getSingleReturnValue(method)); + } } return null; } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index e8b74efd73d0..87e0d2dad351 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -28,7 +28,9 @@ import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiPrimitiveType; +import com.intellij.psi.PsiType; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; @@ -77,15 +79,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState { newState.myEqClasses.add(aClass != null ? new SortedIntSet(aClass.toNativeArray()) : null); } - try { - for (Object o : myVariableStates.keySet()) { - DfaVariableValue dfaVariableValue = (DfaVariableValue)o; - DfaVariableState clone = (DfaVariableState)myVariableStates.get(dfaVariableValue).clone(); - newState.myVariableStates.put(dfaVariableValue, clone); - } - } - catch (CloneNotSupportedException e) { - LOG.error(e); + for (DfaVariableValue dfaVariableValue : myVariableStates.keySet()) { + newState.myVariableStates.put(dfaVariableValue, myVariableStates.get(dfaVariableValue).clone()); } return newState; } @@ -280,13 +275,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { applyCondition(dfaEqual); if (value instanceof DfaVariableValue) { - try { - DfaVariableState newState = (DfaVariableState)getVariableState((DfaVariableValue)value).clone(); - myVariableStates.put(var, newState); - } - catch (CloneNotSupportedException e) { - LOG.error(e); - } + myVariableStates.put(var, getVariableState((DfaVariableValue)value).clone()); } } @@ -782,18 +771,15 @@ public class DfaMemoryStateImpl implements DfaMemoryState { public void flushFields(DataFlowRunner runner) { for (DfaVariableValue field : runner.getFields()) { if (myVariableStates.containsKey(field) || getEqClassIndex(field) >= 0) { - flushVariable(field); - getVariableState(field).setNullable(false); + if (!DfaUtil.isFinalField(field.getPsiVariable())) { + flushWithDependencies(field); + getVariableState(field).setNullable(false); + } } } } public void flushVariable(@NotNull DfaVariableValue variable) { - PsiVariable psiVariable = variable.getPsiVariable(); - if (DfaVariableState.isFinalField(psiVariable)) { - return; - } - flushWithDependencies(variable); } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java index 065464ff47b5..69c7b05f8e2b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java @@ -21,6 +21,7 @@ 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; @@ -85,17 +86,17 @@ public class DfaUtil { return expressions == null ? Collections.<PsiExpression>emptyList() : expressions; } - @Nullable - static Boolean getElementNullability(@Nullable PsiType resultType, @Nullable PsiModifierListOwner owner) { + @NotNull + public static Nullness getElementNullability(@Nullable PsiType resultType, @Nullable PsiModifierListOwner owner) { if (owner == null) { - return null; + return Nullness.UNKNOWN; } if (NullableNotNullManager.isNullable(owner)) { - return Boolean.TRUE; + return Nullness.NULLABLE; } if (NullableNotNullManager.isNotNull(owner)) { - return Boolean.FALSE; + return Nullness.NOT_NULL; } if (resultType != null) { @@ -103,19 +104,51 @@ public class DfaUtil { for (PsiAnnotation annotation : resultType.getAnnotations()) { String qualifiedName = annotation.getQualifiedName(); if (nnn.getNullables().contains(qualifiedName)) { - return Boolean.TRUE; + return Nullness.NULLABLE; } if (nnn.getNotNulls().contains(qualifiedName)) { - return Boolean.FALSE; + return Nullness.NOT_NULL; } } } - return 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 enum Nullness { - NOT_NULL,NULL,UNKNOWN + public static boolean isFinalField(PsiVariable var) { + return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField; } @NotNull @@ -131,7 +164,7 @@ public class DfaUtil { if (result != RunnerResult.OK) { return Nullness.UNKNOWN; } - if (visitor.myNulls.contains(variable) && !visitor.myNotNulls.contains(variable)) return Nullness.NULL; + if (visitor.myNulls.contains(variable) && !visitor.myNotNulls.contains(variable)) return Nullness.NULLABLE; if (visitor.myNotNulls.contains(variable) && !visitor.myNulls.contains(variable)) return Nullness.NOT_NULL; return Nullness.UNKNOWN; } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java index 02e841c89132..7c86f7f4e27b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java @@ -24,11 +24,9 @@ */ package com.intellij.codeInspection.dataFlow; -import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.dataFlow.value.DfaTypeValue; import com.intellij.codeInspection.dataFlow.value.DfaValue; import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; -import com.intellij.codeInspection.nullable.NullableStuffInspection; import com.intellij.psi.*; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; @@ -37,65 +35,28 @@ import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Set; public class DfaVariableState implements Cloneable { private final Set<DfaTypeValue> myInstanceofValues; private final Set<DfaTypeValue> myNotInstanceofValues; - private boolean myNullable = false; - private final boolean myVariableIsDeclaredNotNull; + private Nullness myNullability; public DfaVariableState(@NotNull DfaVariableValue dfaVar) { myInstanceofValues = new HashSet<DfaTypeValue>(); myNotInstanceofValues = new HashSet<DfaTypeValue>(); - PsiVariable var = dfaVar.getPsiVariable(); - Boolean nullability = DfaUtil.getElementNullability(dfaVar.getVariableType(), var); - myNullable = nullability == Boolean.TRUE || var != null && isNullableInitialized(var, true); - myVariableIsDeclaredNotNull = nullability == Boolean.FALSE || var != null && isNullableInitialized(var, false); + + myNullability = dfaVar.getInherentNullability(); } protected DfaVariableState(final DfaVariableState toClone) { myInstanceofValues = new THashSet<DfaTypeValue>(toClone.myInstanceofValues); myNotInstanceofValues = new THashSet<DfaTypeValue>(toClone.myNotInstanceofValues); - myNullable = toClone.myNullable; - myVariableIsDeclaredNotNull = toClone.myVariableIsDeclaredNotNull; - } - - private 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 isFinalField(PsiVariable var) { - return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField; + myNullability = toClone.myNullability; } public boolean isNullable() { - return myNullable; + return myNullability == Nullness.NULLABLE; } private boolean checkInstanceofValue(DfaTypeValue dfaType) { @@ -113,7 +74,9 @@ public class DfaVariableState implements Cloneable { } public boolean setInstanceofValue(DfaTypeValue dfaType) { - myNullable |= dfaType.isNullable(); + if (dfaType.isNullable()) { + myNullability = Nullness.NULLABLE; + } if (dfaType.getType() instanceof PsiPrimitiveType) return true; @@ -146,10 +109,10 @@ public class DfaVariableState implements Cloneable { DfaVariableState aState = (DfaVariableState) obj; return myInstanceofValues.equals(aState.myInstanceofValues) && myNotInstanceofValues.equals(aState.myNotInstanceofValues) && - myNullable == aState.myNullable; + myNullability == aState.myNullability; } - protected Object clone() throws CloneNotSupportedException { + protected DfaVariableState clone() { return new DfaVariableState(this); } @@ -169,16 +132,18 @@ public class DfaVariableState implements Cloneable { buf.append("{").append(dfaTypeValue).append("}"); if (iterator.hasNext()) buf.append(", "); } - buf.append(", nullable=").append(myNullable); + buf.append(", nullable=").append(myNullability); return buf.toString(); } public boolean isNotNull() { - return myVariableIsDeclaredNotNull; + return myNullability == Nullness.NOT_NULL; } public void setNullable(final boolean nullable) { - myNullable = nullable; + if (myNullability != Nullness.NOT_NULL) { + myNullability = nullable ? Nullness.NULLABLE : Nullness.UNKNOWN; + } } public void setValue(DfaValue value) { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/Nullness.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/Nullness.java new file mode 100644 index 000000000000..3dd099b0c1d3 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/Nullness.java @@ -0,0 +1,23 @@ +/* + * 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; + +/** +* @author cdr +*/ +public enum Nullness { + NOT_NULL, NULLABLE,UNKNOWN +} diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index a216be6f3cb0..54df852bfbab 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -40,26 +40,26 @@ public class StandardInstructionVisitor extends InstructionVisitor { private final Set<BinopInstruction> myCanBeNullInInstanceof = new THashSet<BinopInstruction>(); private final Set<PsiElement> myNotToReportReachability = new THashSet<PsiElement>(); private final Set<InstanceofInstruction> myUsefulInstanceofs = new THashSet<InstanceofInstruction>(); - private final FactoryMap<MethodCallInstruction, Map<PsiExpression, Boolean>> myParametersNullability = new FactoryMap<MethodCallInstruction, Map<PsiExpression, Boolean>>() { + private final FactoryMap<MethodCallInstruction, Map<PsiExpression, Nullness>> myParametersNullability = new FactoryMap<MethodCallInstruction, Map<PsiExpression, Nullness>>() { @Nullable @Override - protected Map<PsiExpression, Boolean> create(MethodCallInstruction key) { + protected Map<PsiExpression, Nullness> create(MethodCallInstruction key) { return calcParameterNullability(key.getCallExpression()); } }; - private final FactoryMap<MethodCallInstruction, Boolean> myReturnTypeNullability = new FactoryMap<MethodCallInstruction, Boolean>() { + private final FactoryMap<MethodCallInstruction, Nullness> myReturnTypeNullability = new FactoryMap<MethodCallInstruction, Nullness>() { @Override - protected Boolean create(MethodCallInstruction key) { + protected Nullness create(MethodCallInstruction key) { final PsiCallExpression callExpression = key.getCallExpression(); if (callExpression instanceof PsiNewExpression) { - return Boolean.FALSE; + return Nullness.NOT_NULL; } return callExpression != null ? DfaUtil.getElementNullability(key.getResultType(), callExpression.resolveMethod()) : null; } }; - private static Map<PsiExpression, Boolean> calcParameterNullability(@Nullable PsiCallExpression callExpression) { + private static Map<PsiExpression, Nullness> calcParameterNullability(@Nullable PsiCallExpression callExpression) { PsiExpressionList argumentList = callExpression == null ? null : callExpression.getArgumentList(); if (argumentList != null) { JavaResolveResult result = callExpression.resolveMethodGenerics(); @@ -72,7 +72,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { boolean varArg = isVarArgCall(method, substitutor, args, parameters); int checkedCount = Math.min(args.length, parameters.length) - (varArg ? 1 : 0); - Map<PsiExpression, Boolean> map = ContainerUtil.newHashMap(); + 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])); } @@ -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) == Boolean.FALSE) { + if (DfaUtil.getElementNullability(var.getVariableType(), psiVariable) == Nullness.NOT_NULL) { if (!memState.applyNotNull(dfaSource)) { onAssigningToNotNullableVariable(instruction); } @@ -183,12 +183,12 @@ public class StandardInstructionVisitor extends InstructionVisitor { @Override public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final PsiExpression[] args = instruction.getArgs(); - Map<PsiExpression, Boolean> map = myParametersNullability.get(instruction); + Map<PsiExpression, Nullness> map = myParametersNullability.get(instruction); final DfaNotNullValue.Factory factory = runner.getFactory().getNotNullFactory(); for (int i = 0; i < args.length; i++) { final DfaValue arg = memState.pop(); PsiExpression expr = args[(args.length - i - 1)]; - if (map.get(expr) == Boolean.FALSE) { + if (map.get(expr) == Nullness.NOT_NULL) { if (!memState.applyNotNull(arg)) { onPassingNullParameter(expr); if (arg instanceof DfaVariableValue) { @@ -196,7 +196,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { } } } - else if (map.containsKey(expr) && map.get(expr) == null && !memState.checkNotNullable(arg)) { + else if (map.get(expr) == Nullness.UNKNOWN && !memState.checkNotNullable(arg)) { onPassingNullParameterToNonAnnotated(runner, expr); } } @@ -230,8 +230,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { final PsiType type = instruction.getResultType(); final MethodCallInstruction.MethodType methodType = instruction.getMethodType(); if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) { - @Nullable final Boolean nullability = myReturnTypeNullability.get(instruction); - return factory.createTypeValueWithNullability(type, nullability); + return factory.createTypeValueWithNullability(type, myReturnTypeNullability.get(instruction)); } if (methodType == MethodCallInstruction.MethodType.UNBOXING) { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java index 8ae655d475b9..d715f7637c9d 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java @@ -67,7 +67,7 @@ public class ValuableDataFlowRunner extends AnnotationsAwareDataFlowRunner { return myValue; } - protected Object clone() throws CloneNotSupportedException { + protected ValuableDfaVariableState clone() { return new ValuableDfaVariableState(this); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java index 22f98623447d..5f65a86caeed 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java @@ -72,6 +72,10 @@ public class MethodCallInstruction extends Instruction { } } + public void setShouldFlushFields(boolean shouldFlushFields) { + myShouldFlushFields = shouldFlushFields; + } + @Nullable public PsiType getResultType() { return myType; diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java index 83c9f5efaafb..d68679796fc4 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java @@ -24,9 +24,11 @@ */ package com.intellij.codeInspection.dataFlow.value; +import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; +import com.intellij.psi.util.PsiTreeUtil; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,8 +51,8 @@ public class DfaValueFactory { myRelationFactory = new DfaRelationValue.Factory(this); } - public DfaValue createTypeValueWithNullability(@Nullable PsiType type, @Nullable Boolean nullability) { - return nullability == Boolean.FALSE ? getNotNullFactory().create(type) : getTypeFactory().create(type, nullability == Boolean.TRUE); + public DfaValue createTypeValueWithNullability(@Nullable PsiType type, Nullness nullability) { + return nullability == Nullness.NOT_NULL ? getNotNullFactory().create(type) : getTypeFactory().create(type, nullability == Nullness.NULLABLE); } int createID() { @@ -133,7 +135,7 @@ public class DfaValueFactory { } } - if (isEffectivelyUnqualified(referenceExpression)) { + if (!variable.hasModifierProperty(PsiModifier.VOLATILE) && isEffectivelyUnqualified(referenceExpression)) { return getVarFactory().createVariableValue(variable, referenceExpression.getType(), false, null, false); } @@ -152,9 +154,20 @@ public class DfaValueFactory { return null; } - private static boolean isEffectivelyUnqualified(PsiReferenceExpression refExpression) { + public static boolean isEffectivelyUnqualified(PsiReferenceExpression refExpression) { PsiExpression qualifier = refExpression.getQualifierExpression(); - return qualifier == null || qualifier instanceof PsiThisExpression; + if (qualifier == null) { + return true; + } + if (qualifier instanceof PsiThisExpression){ + final PsiJavaCodeReferenceElement thisQualifier = ((PsiThisExpression)qualifier).getQualifier(); + if (thisQualifier == null) return true; + final PsiClass innerMostClass = PsiTreeUtil.getParentOfType(refExpression, PsiClass.class); + if (innerMostClass == thisQualifier.resolve()) { + return true; + } + } + return false; } private final DfaVariableValue.Factory myVarFactory; diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java index 1c0434dd6ddc..f56fcd8290eb 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java @@ -24,6 +24,8 @@ */ package com.intellij.codeInspection.dataFlow.value; +import com.intellij.codeInspection.dataFlow.DfaUtil; +import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.psi.PsiType; import com.intellij.psi.PsiVariable; import com.intellij.util.containers.HashMap; @@ -92,6 +94,7 @@ public class DfaVariableValue extends DfaValue { @Nullable private DfaVariableValue myQualifier; private boolean myIsNegated; private boolean myViaMethods; + private Nullness myInherentNullability; private DfaVariableValue(PsiVariable variable, PsiType varType, boolean isNegated, DfaValueFactory factory, @Nullable DfaVariableValue qualifier, boolean viaMethods) { super(factory); @@ -115,7 +118,7 @@ public class DfaVariableValue extends DfaValue { @Nullable public PsiType getVariableType() { - return myVariable == null ? null : myVariable.getType(); + return myVarType; } public boolean isNegated() { @@ -147,4 +150,25 @@ public class DfaVariableValue extends DfaValue { public boolean isViaMethods() { return myViaMethods; } + + public Nullness getInherentNullability() { + if (myInherentNullability != null) { + return myInherentNullability; + } + + PsiVariable var = getPsiVariable(); + Nullness nullability = DfaUtil.getElementNullability(getVariableType(), var); + if (nullability == Nullness.UNKNOWN && var != null) { + if (DfaUtil.isNullableInitialized(var, true)) { + nullability = Nullness.NULLABLE; + } else if (DfaUtil.isNullableInitialized(var, false)) { + nullability = Nullness.NOT_NULL; + } + } + + myInherentNullability = nullability; + + return nullability; + } + } diff --git a/java/java-impl/src/com/intellij/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java b/java/java-impl/src/com/intellij/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java index 309fefcddba8..6cc4a8cdc709 100644 --- a/java/java-impl/src/com/intellij/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java +++ b/java/java-impl/src/com/intellij/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java @@ -50,15 +50,12 @@ public class FileHeaderChecker { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.defaultFileTemplateUsage.FileHeaderChecker"); static ProblemDescriptor checkFileHeader(@NotNull final PsiFile file, final InspectionManager manager, boolean onTheFly) { - FileTemplate template = FileTemplateManager.getInstance().getDefaultTemplate(FileTemplateManager.FILE_HEADER_TEMPLATE_NAME); TIntObjectHashMap<String> offsetToProperty = new TIntObjectHashMap<String>(); - String templateText = template.getText().trim(); - String regex = templateToRegex(templateText, offsetToProperty, file.getProject()); - regex = StringUtil.replace(regex, "with", "(?:with|by)"); - regex = ".*("+regex+").*"; - String fileText = file.getText(); - Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); - Matcher matcher = pattern.matcher(fileText); + Pattern pattern = getTemplatePattern(FileTemplateManager.getInstance() + .getDefaultTemplate(FileTemplateManager.FILE_HEADER_TEMPLATE_NAME), + file.getProject(), offsetToProperty + ); + Matcher matcher = pattern.matcher(file.getText()); if (matcher.matches()) { final int startOffset = matcher.start(1); final int endOffset = matcher.end(1); @@ -83,6 +80,14 @@ public class FileHeaderChecker { return null; } + public static Pattern getTemplatePattern(FileTemplate template, Project project, TIntObjectHashMap<String> offsetToProperty) { + String templateText = template.getText().trim(); + String regex = templateToRegex(templateText, offsetToProperty, project); + regex = StringUtil.replace(regex, "with", "(?:with|by)"); + regex = ".*("+regex+").*"; + return Pattern.compile(regex, Pattern.DOTALL); + } + private static Properties computeProperties(final Matcher matcher, final TIntObjectHashMap<String> offsetToProperty) { Properties properties = new Properties(FileTemplateManager.getInstance().getDefaultProperties()); int[] offsets = offsetToProperty.keys(); diff --git a/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java b/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java index 01fa24239dff..e65e8e4f2db5 100644 --- a/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/ex/GlobalJavaInspectionContextImpl.java @@ -135,7 +135,7 @@ public class GlobalJavaInspectionContextImpl extends GlobalJavaInspectionContext else if (entry instanceof LibraryOrderEntry) { final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry; final Library library = libraryOrderEntry.getLibrary(); - if (library == null || library.getFiles(OrderRootType.CLASSES).length != library.getUrls(OrderRootType.CLASSES).length) { + if (library == null || library.getFiles(OrderRootType.CLASSES).length < library.getUrls(OrderRootType.CLASSES).length) { System.err.println(InspectionsBundle.message("offline.inspections.library.was.not.resolved", libraryOrderEntry.getPresentableName(), module.getName())); } diff --git a/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java b/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java index 66444fe29e9e..6923ccceb060 100644 --- a/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java @@ -466,7 +466,7 @@ public class NullableStuffInspection extends BaseLocalInspectionTool { } public static List<PsiExpression> findAllConstructorInitializers(PsiField field) { - final List<PsiExpression> result = ContainerUtil.createEmptyCOWList(); + final List<PsiExpression> result = ContainerUtil.createLockFreeCopyOnWriteList(); ContainerUtil.addIfNotNull(result, field.getInitializer()); PsiClass containingClass = field.getContainingClass(); diff --git a/java/java-impl/src/com/intellij/codeInspection/sillyAssignment/SillyAssignmentInspection.java b/java/java-impl/src/com/intellij/codeInspection/sillyAssignment/SillyAssignmentInspection.java index 29ae2b877666..6fd1d33a37f5 100644 --- a/java/java-impl/src/com/intellij/codeInspection/sillyAssignment/SillyAssignmentInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/sillyAssignment/SillyAssignmentInspection.java @@ -21,9 +21,11 @@ import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * User: anna @@ -120,7 +122,9 @@ public class SillyAssignmentInspection extends BaseJavaLocalInspectionTool { /** * @return true if both expressions resolve to the same variable/class or field in the same instance of the class */ - private static boolean sameInstanceReferences(PsiReferenceExpression lRef, PsiReferenceExpression rRef, PsiManager manager) { + private static boolean sameInstanceReferences(@Nullable PsiJavaCodeReferenceElement lRef, @Nullable PsiJavaCodeReferenceElement rRef, PsiManager manager) { + if (lRef == null && rRef == null) return true; + if (lRef == null || rRef == null) return false; PsiElement lResolved = lRef.resolve(); PsiElement rResolved = rRef.resolve(); if (!manager.areElementsEquivalent(lResolved, rResolved)) return false; @@ -128,15 +132,36 @@ public class SillyAssignmentInspection extends BaseJavaLocalInspectionTool { final PsiVariable variable = (PsiVariable)lResolved; if (variable.hasModifierProperty(PsiModifier.STATIC)) return true; - PsiExpression lQualifier = lRef.getQualifierExpression(); - PsiExpression rQualifier = rRef.getQualifierExpression(); - if (lQualifier instanceof PsiReferenceExpression && rQualifier instanceof PsiReferenceExpression) { - return sameInstanceReferences((PsiReferenceExpression)lQualifier, (PsiReferenceExpression)rQualifier, manager); + final PsiElement lQualifier = lRef.getQualifier(); + final PsiElement rQualifier = rRef.getQualifier(); + if (lQualifier instanceof PsiJavaCodeReferenceElement && rQualifier instanceof PsiJavaCodeReferenceElement) { + return sameInstanceReferences((PsiJavaCodeReferenceElement)lQualifier, (PsiJavaCodeReferenceElement)rQualifier, manager); } + if (Comparing.equal(lQualifier, rQualifier)) return true; boolean lThis = lQualifier == null || lQualifier instanceof PsiThisExpression || lQualifier instanceof PsiSuperExpression; boolean rThis = rQualifier == null || rQualifier instanceof PsiThisExpression || rQualifier instanceof PsiSuperExpression; - return lThis && rThis; + if (lThis && rThis) { + final PsiJavaCodeReferenceElement llQualifier = getQualifier(lQualifier); + final PsiJavaCodeReferenceElement rrQualifier = getQualifier(rQualifier); + return sameInstanceReferences(llQualifier, rrQualifier, manager); + } + return false; } + private static PsiJavaCodeReferenceElement getQualifier(PsiElement qualifier) { + if (qualifier instanceof PsiThisExpression) { + final PsiJavaCodeReferenceElement thisQualifier = ((PsiThisExpression)qualifier).getQualifier(); + if (thisQualifier != null) { + final PsiClass innerMostClass = PsiTreeUtil.getParentOfType(thisQualifier, PsiClass.class); + if (innerMostClass == thisQualifier.resolve()) { + return null; + } + } + return thisQualifier; + } else if (qualifier != null) { + return ((PsiSuperExpression)qualifier).getQualifier(); + } + return null; + } } diff --git a/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java b/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java index 833a7d06978b..684d99ca481b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java @@ -437,6 +437,7 @@ public class UncheckedWarningLocalInspection extends BaseJavaLocalInspectionTool public Boolean visitClassType(PsiClassType classType) { PsiClass psiClass = classType.resolve(); if (psiClass instanceof PsiTypeParameter) { + if (((PsiTypeParameter)psiClass).getOwner() == method) return Boolean.FALSE; return substitutor.substitute((PsiTypeParameter)psiClass) == null ? Boolean.TRUE : Boolean.FALSE; } PsiType[] parameters = classType.getParameters(); |