All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.codeInspection.dataFlow.DfaUtil Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition java-analysis-impl library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * 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.dataFlow;

import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.openapi.util.MultiValuesMap;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FList;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Gregory.Shrago
 */
public class DfaUtil {

  @Nullable("null means DFA analysis has failed (too complex to analyze)")
  public static Collection getCachedVariableValues(@Nullable final PsiVariable variable, @Nullable final PsiElement context) {
    if (variable == null || context == null) return Collections.emptyList();

    final PsiElement codeBlock = DfaPsiUtil.getEnclosingCodeBlock(variable, context);
    if (codeBlock == null) return Collections.emptyList();

    final Map value = getCachedPlaceResults(codeBlock);
    if (value == null) return null;

    ValuableInstructionVisitor.PlaceResult placeResult = value.get(context);
    final Collection> concatenations = placeResult == null ? null : placeResult.myValues.get(variable);
    if (concatenations != null) {
      return ContainerUtil.map(concatenations, new Function, PsiExpression>() {
        @Override
        public PsiExpression fun(FList expressions) {
          return concatenateExpressions(expressions);
        }
      });
    }
    return Collections.emptyList();
  }

  @Nullable("null means DFA analysis has failed (too complex to analyze)")
  private static Map getCachedPlaceResults(@NotNull final PsiElement codeBlock) {
    return CachedValuesManager.getCachedValue(codeBlock, new CachedValueProvider>() {
      @Override
      public Result> compute() {
        final ValuableInstructionVisitor visitor = new ValuableInstructionVisitor();
        RunnerResult runnerResult = new ValuableDataFlowRunner(codeBlock).analyzeMethod(codeBlock, visitor);
        return Result.create(runnerResult == RunnerResult.OK ? visitor.myResults : null, codeBlock);
      }
    });
  }

  @NotNull
  public static Nullness checkNullness(@Nullable final PsiVariable variable, @Nullable final PsiElement context) {
    if (variable == null || context == null) return Nullness.UNKNOWN;

    final PsiElement codeBlock = DfaPsiUtil.getEnclosingCodeBlock(variable, context);
    Map results = codeBlock == null ? null : getCachedPlaceResults(codeBlock);
    ValuableInstructionVisitor.PlaceResult placeResult = results == null ? null : results.get(context);
    if (placeResult == null) {
      return Nullness.UNKNOWN;
    }
    if (placeResult.myNulls.contains(variable) && !placeResult.myNotNulls.contains(variable)) return Nullness.NULLABLE;
    if (placeResult.myNotNulls.contains(variable) && !placeResult.myNulls.contains(variable)) return Nullness.NOT_NULL;
    return Nullness.UNKNOWN;
  }

  @NotNull
  public static Collection getPossibleInitializationElements(final PsiElement qualifierExpression) {
    if (qualifierExpression instanceof PsiMethodCallExpression) {
      return Collections.singletonList(qualifierExpression);
    }
    if (qualifierExpression instanceof PsiReferenceExpression) {
      final PsiElement targetElement = ((PsiReferenceExpression)qualifierExpression).resolve();
      if (!(targetElement instanceof PsiVariable)) {
        return Collections.emptyList();
      }
      final Collection variableValues = getCachedVariableValues((PsiVariable)targetElement, qualifierExpression);
      if (variableValues == null || variableValues.isEmpty()) {
        return DfaPsiUtil.getVariableAssignmentsInFile((PsiVariable)targetElement, false, qualifierExpression);
      }
      return variableValues;
    }
    if (qualifierExpression instanceof PsiLiteralExpression) {
      return Collections.singletonList(qualifierExpression);
    }
    return Collections.emptyList();
  }

  @Nullable
  static PsiElement getClosureInside(Instruction instruction) {
    if (instruction instanceof MethodCallInstruction) {
      PsiCallExpression anchor = ((MethodCallInstruction)instruction).getCallExpression();
      if (anchor instanceof PsiNewExpression) {
        return ((PsiNewExpression)anchor).getAnonymousClass();
      }
    }
    else if (instruction instanceof LambdaInstruction) {
      return ((LambdaInstruction)instruction).getLambdaExpression();
    }
    else if (instruction instanceof EmptyInstruction) {
      PsiElement anchor = ((EmptyInstruction)instruction).getAnchor();
      if (anchor instanceof PsiClass) {
        return anchor;
      }
    }
    return null;
  }

  @NotNull
  public static Nullness inferMethodNullity(PsiMethod method) {
    final PsiCodeBlock body = method.getBody();
    if (body == null || PsiUtil.resolveClassInType(method.getReturnType()) == null) {
      return Nullness.UNKNOWN;
    }

    final AtomicBoolean hasNulls = new AtomicBoolean();
    final AtomicBoolean hasNotNulls = new AtomicBoolean();
    final AtomicBoolean hasUnknowns = new AtomicBoolean();

    final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner();
    final RunnerResult rc = dfaRunner.analyzeMethod(body, new StandardInstructionVisitor() {
      @Override
      public DfaInstructionState[] visitCheckReturnValue(CheckReturnValueInstruction instruction,
                                                         DataFlowRunner runner,
                                                         DfaMemoryState memState) {
        DfaValue returned = memState.peek();
        if (memState.isNull(returned)) {
          hasNulls.set(true);
        }
        else if (memState.isNotNull(returned)) {
          hasNotNulls.set(true);
        }
        else {
          hasUnknowns.set(true);
        }
        return super.visitCheckReturnValue(instruction, runner, memState);
      }
    });

    if (rc == RunnerResult.OK) {
      if (hasNulls.get()) {
        return InferenceFromSourceUtil.suppressNullable(method) ? Nullness.UNKNOWN : Nullness.NULLABLE;
      }
      if (hasNotNulls.get() && !hasUnknowns.get()) {
        return Nullness.NOT_NULL;
      }
    }

    return Nullness.UNKNOWN;
  }

  private static class ValuableInstructionVisitor extends StandardInstructionVisitor {
    final Map myResults = ContainerUtil.newHashMap();

    static class PlaceResult {
      final MultiValuesMap> myValues = new MultiValuesMap>(true);
      final Set myNulls = new THashSet();
      final Set myNotNulls = new THashSet();
    }

    @Override
    public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
      PsiExpression place = instruction.getPlace();
      if (place != null) {
        PlaceResult result = myResults.get(place);
        if (result == null) {
          myResults.put(place, result = new PlaceResult());
        }
        final Map map = ((ValuableDataFlowRunner.MyDfaMemoryState)memState).getVariableStates();
        for (Map.Entry entry : map.entrySet()) {
          ValuableDataFlowRunner.ValuableDfaVariableState state = (ValuableDataFlowRunner.ValuableDfaVariableState)entry.getValue();
          DfaVariableValue variableValue = entry.getKey();
          final FList concatenation = state.myConcatenation;
          if (!concatenation.isEmpty() && variableValue.getQualifier() == null) {
            PsiModifierListOwner element = variableValue.getPsiVariable();
            if (element instanceof PsiVariable) {
              result.myValues.put((PsiVariable)element, concatenation);
            }
          }
        }
        DfaValue value = instruction.getValue();
        if (value instanceof DfaVariableValue && ((DfaVariableValue)value).getQualifier() == null) {
          PsiModifierListOwner element = ((DfaVariableValue)value).getPsiVariable();
          if (element instanceof PsiVariable) {
            if (memState.isNotNull(value)) {
              result.myNotNulls.add((PsiVariable)element);
            }
            if (memState.isNull(value)) {
              result.myNulls.add((PsiVariable)element);
            }
          }
        }
      }
      return super.visitPush(instruction, runner, memState);
    }

    @Override
    public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState _memState) {
      final Instruction nextInstruction = runner.getInstruction(instruction.getIndex() + 1);

      ValuableDataFlowRunner.MyDfaMemoryState memState = (ValuableDataFlowRunner.MyDfaMemoryState)_memState;
      final DfaValue dfaSource = memState.pop();
      final DfaValue dfaDest = memState.pop();

      if (dfaDest instanceof DfaVariableValue) {
        DfaVariableValue var = (DfaVariableValue)dfaDest;
        final PsiExpression rightValue = instruction.getRExpression();
        final PsiElement parent = rightValue == null ? null : rightValue.getParent();
        final IElementType type = parent instanceof PsiAssignmentExpression
                                  ? ((PsiAssignmentExpression)parent).getOperationTokenType() : JavaTokenType.EQ;
        // store current value - to use in case of '+='
        final FList prevValue = ((ValuableDataFlowRunner.ValuableDfaVariableState)memState.getVariableState(var)).myConcatenation;
        memState.setVarValue(var, dfaSource);
        // state may have been changed so re-retrieve it
        final ValuableDataFlowRunner.ValuableDfaVariableState curState = (ValuableDataFlowRunner.ValuableDfaVariableState)memState.getVariableState(var);
        final FList curValue = curState.myConcatenation;
        final FList nextValue;
        if (type == JavaTokenType.PLUSEQ && !prevValue.isEmpty()) {
          nextValue = prevValue.prepend(rightValue);
        }
        else {
          nextValue = curValue.isEmpty() ? curValue.prepend(rightValue) : curValue;
        }
        memState.setVariableState(var, curState.withExpression(nextValue));
      }
      memState.push(dfaDest);
      return new DfaInstructionState[]{new DfaInstructionState(nextInstruction, memState)};
    }
  }

  private static PsiExpression concatenateExpressions(FList concatenation) {
    if (concatenation.size() == 1) {
      return concatenation.getHead();
    }
    String text = StringUtil
      .join(ContainerUtil.reverse(new ArrayList(concatenation)), new Function() {
        @Override
        public String fun(PsiExpression expression) {
          return expression.getText();
        }
      }, "+");
    try {
      return JavaPsiFacade.getElementFactory(concatenation.getHead().getProject()).createExpressionFromText(text, concatenation.getHead());
    }
    catch (IncorrectOperationException e) {
      return concatenation.getHead();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy