blob: fa2217a2918b3a0824e49666c16c507f81f5c231 [file] [log] [blame]
/*
* 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.
*/
/*
* Created by IntelliJ IDEA.
* User: max
* Date: Jan 28, 2002
* Time: 6:31:08 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij.codeInspection.dataFlow.value;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Trinity;
import com.intellij.psi.*;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DfaVariableValue extends DfaValue {
public static class Factory {
private final MultiMap<Trinity<Boolean,String,DfaVariableValue>,DfaVariableValue> myExistingVars = new MultiMap<Trinity<Boolean, String, DfaVariableValue>, DfaVariableValue>();
private final DfaValueFactory myFactory;
Factory(DfaValueFactory factory) {
myFactory = factory;
}
public DfaVariableValue createVariableValue(PsiVariable myVariable, boolean isNegated) {
return createVariableValue(myVariable, myVariable.getType(), isNegated, null);
}
@NotNull
public DfaVariableValue createVariableValue(@NotNull PsiModifierListOwner myVariable,
@Nullable PsiType varType,
boolean isNegated,
@Nullable DfaVariableValue qualifier) {
Trinity<Boolean,String,DfaVariableValue> key = Trinity.create(isNegated, ((PsiNamedElement)myVariable).getName(), qualifier);
for (DfaVariableValue aVar : myExistingVars.get(key)) {
if (aVar.hardEquals(myVariable, varType, isNegated, qualifier)) return aVar;
}
DfaVariableValue result = new DfaVariableValue(myVariable, varType, isNegated, myFactory, qualifier);
myExistingVars.putValue(key, result);
while (qualifier != null) {
qualifier.myDependents.add(result);
qualifier = qualifier.getQualifier();
}
return result;
}
public List<DfaVariableValue> getAllQualifiedBy(DfaVariableValue value) {
return value.myDependents;
}
}
private final PsiModifierListOwner myVariable;
private final PsiType myVarType;
@Nullable private final DfaVariableValue myQualifier;
private DfaVariableValue myNegatedValue;
private final boolean myIsNegated;
private Nullness myInherentNullability;
private final DfaTypeValue myTypeValue;
private final List<DfaVariableValue> myDependents = new SmartList<DfaVariableValue>();
private DfaVariableValue(@NotNull PsiModifierListOwner variable, PsiType varType, boolean isNegated, DfaValueFactory factory, @Nullable DfaVariableValue qualifier) {
super(factory);
myVariable = variable;
myIsNegated = isNegated;
myQualifier = qualifier;
myVarType = varType;
myTypeValue = varType == null ? null : (DfaTypeValue)myFactory.createTypeValue(varType, Nullness.UNKNOWN);
}
@Nullable
public DfaTypeValue getTypeValue() {
return myTypeValue;
}
@Nullable
public PsiModifierListOwner getPsiVariable() {
return myVariable;
}
@Nullable
public PsiType getVariableType() {
return myVarType;
}
public boolean isNegated() {
return myIsNegated;
}
@Nullable
public DfaVariableValue getNegatedValue() {
return myNegatedValue;
}
@Override
public DfaVariableValue createNegated() {
if (myNegatedValue != null) {
return myNegatedValue;
}
return myNegatedValue = myFactory.getVarFactory().createVariableValue(myVariable, myVarType, !myIsNegated, myQualifier);
}
@SuppressWarnings({"HardCodedStringLiteral"})
public String toString() {
return (myIsNegated ? "!" : "") + ((PsiNamedElement)myVariable).getName() + (myQualifier == null ? "" : "|" + myQualifier.toString());
}
private boolean hardEquals(PsiModifierListOwner psiVar, PsiType varType, boolean negated, DfaVariableValue qualifier) {
return psiVar == myVariable &&
Comparing.equal(TypeConversionUtil.erasure(varType), TypeConversionUtil.erasure(myVarType)) &&
negated == myIsNegated &&
(myQualifier == null ? qualifier == null : myQualifier.hardEquals(qualifier.getPsiVariable(), qualifier.getVariableType(),
qualifier.isNegated(), qualifier.getQualifier()));
}
@Nullable
public DfaVariableValue getQualifier() {
return myQualifier;
}
public Nullness getInherentNullability() {
if (myInherentNullability != null) {
return myInherentNullability;
}
return myInherentNullability = calcInherentNullability();
}
private Nullness calcInherentNullability() {
PsiModifierListOwner var = getPsiVariable();
Nullness nullability = DfaPsiUtil.getElementNullability(getVariableType(), var);
if (nullability != Nullness.UNKNOWN) {
return nullability;
}
if (var instanceof PsiField && DfaPsiUtil.isFinalField((PsiVariable)var)) {
List<PsiExpression> initializers = DfaPsiUtil.findAllConstructorInitializers((PsiField)var);
if (initializers.isEmpty()) {
return Nullness.UNKNOWN;
}
boolean hasUnknowns = false;
for (PsiExpression expression : initializers) {
if (!(expression instanceof PsiReferenceExpression)) {
return Nullness.UNKNOWN;
}
PsiElement target = ((PsiReferenceExpression)expression).resolve();
if (!(target instanceof PsiParameter)) {
return Nullness.UNKNOWN;
}
if (NullableNotNullManager.isNullable((PsiParameter)target)) {
return Nullness.NULLABLE;
}
if (!NullableNotNullManager.isNotNull((PsiParameter)target)) {
hasUnknowns = true;
}
}
return hasUnknowns ? Nullness.UNKNOWN : Nullness.NOT_NULL;
}
return Nullness.UNKNOWN;
}
public boolean isFlushableByCalls() {
if (myVariable instanceof PsiLocalVariable || myVariable instanceof PsiParameter) return false;
if (myVariable instanceof PsiVariable && myVariable.hasModifierProperty(PsiModifier.FINAL)) {
return myQualifier != null && myQualifier.isFlushableByCalls();
}
return true;
}
}