diff options
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java')
-rw-r--r-- | java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java new file mode 100644 index 000000000000..ec9e02fce92d --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -0,0 +1,170 @@ +/* + * 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.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); + //noinspection unchecked + return Condition.FALSE; + } + } + + 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) { + 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; + } + + +} |