blob: 9b32f8a4c00070e2a6f501ad568188f489ba1253 [file] [log] [blame]
/*
* Copyright 2000-2014 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.value;
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @author peter
*/
public class DfaExpressionFactory {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.value.DfaExpressionFactory");
private static final Condition<String> FALSE_GETTERS = parseFalseGetters();
private static Condition<String> parseFalseGetters() {
try {
final Pattern pattern = Pattern.compile(Registry.stringValue("ide.dfa.getters.with.side.effects"));
return new Condition<String>() {
@Override
public boolean value(String s) {
return pattern.matcher(s).matches();
}
};
}
catch (Exception e) {
LOG.error(e);
return Conditions.alwaysFalse();
}
}
private final DfaValueFactory myFactory;
private Map<Integer, PsiVariable> myMockIndices = ContainerUtil.newHashMap();
public DfaExpressionFactory(DfaValueFactory factory) {
myFactory = factory;
}
@Nullable
public DfaValue getExpressionDfaValue(@Nullable PsiExpression expression) {
if (expression == null) return null;
if (expression instanceof PsiParenthesizedExpression) {
return getExpressionDfaValue(((PsiParenthesizedExpression)expression).getExpression());
}
if (expression instanceof PsiArrayAccessExpression) {
PsiExpression arrayExpression = ((PsiArrayAccessExpression)expression).getArrayExpression();
DfaValue qualifier = getExpressionDfaValue(arrayExpression);
if (qualifier instanceof DfaVariableValue) {
PsiVariable indexVar = getArrayIndexVariable(((PsiArrayAccessExpression)expression).getIndexExpression());
if (indexVar != null) {
return myFactory.getVarFactory().createVariableValue(indexVar, expression.getType(), false, (DfaVariableValue)qualifier);
}
}
return null;
}
if (expression instanceof PsiMethodCallExpression) {
return createReferenceValue(((PsiMethodCallExpression)expression).getMethodExpression());
}
if (expression instanceof PsiReferenceExpression) {
return createReferenceValue((PsiReferenceExpression)expression);
}
if (expression instanceof PsiLiteralExpression) {
return myFactory.createLiteralValue((PsiLiteralExpression)expression);
}
if (expression instanceof PsiNewExpression) {
return myFactory.createTypeValue(expression.getType(), Nullness.NOT_NULL);
}
final Object value = JavaConstantExpressionEvaluator.computeConstantExpression(expression, false);
PsiType type = expression.getType();
if (value != null && type != null) {
if (value instanceof String) {
return myFactory.createTypeValue(type, Nullness.NOT_NULL); // Non-null string literal.
}
return myFactory.getConstFactory().createFromValue(value, type, null);
}
return null;
}
private DfaValue createReferenceValue(@NotNull PsiReferenceExpression refExpr) {
PsiModifierListOwner var = getAccessedVariableOrGetter(refExpr.resolve());
if (var == null) {
return null;
}
if (!var.hasModifierProperty(PsiModifier.VOLATILE) && !var.hasModifierProperty(PsiModifier.TRANSIENT)) {
if (var instanceof PsiVariable && var.hasModifierProperty(PsiModifier.FINAL)) {
DfaValue constValue = myFactory.getConstFactory().create((PsiVariable)var);
if (constValue != null) return constValue;
}
if (DfaValueFactory.isEffectivelyUnqualified(refExpr)) {
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), false, null);
}
DfaValue qualifierValue = getExpressionDfaValue(refExpr.getQualifierExpression());
if (qualifierValue instanceof DfaVariableValue) {
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), false, (DfaVariableValue)qualifierValue);
}
}
PsiType type = refExpr.getType();
return myFactory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, var));
}
@Nullable
private static PsiModifierListOwner getAccessedVariableOrGetter(final PsiElement target) {
if (target instanceof PsiVariable) {
return (PsiVariable)target;
}
if (target instanceof PsiMethod) {
if (PropertyUtil.isSimplePropertyGetter((PsiMethod)target)) {
String qName = PsiUtil.getMemberQualifiedName((PsiMethod)target);
if (qName == null || !FALSE_GETTERS.value(qName)) {
return (PsiMethod)target;
}
}
}
return null;
}
@Nullable
private PsiVariable getArrayIndexVariable(@Nullable PsiExpression indexExpression) {
Object constant = JavaConstantExpressionEvaluator.computeConstantExpression(indexExpression, false);
if (constant instanceof Integer && ((Integer)constant).intValue() >= 0) {
PsiVariable mockVar = myMockIndices.get(constant);
if (mockVar == null) {
mockVar = JavaPsiFacade.getElementFactory(indexExpression.getProject()).createField("$array$index$" + constant, PsiType.INT);
myMockIndices.put((Integer)constant, mockVar);
}
return mockVar;
}
return null;
}
}