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

org.jetbrains.plugins.groovy.lang.psi.impl.GrReassignedLocalVarsChecker Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition groovy-psi library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * 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 org.jetbrains.plugins.groovy.lang.psi.impl;

import com.intellij.openapi.util.NullableComputable;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiType;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;

import java.util.Collection;
import java.util.Set;

/**
 * @author Max Medvedev
 */
public class GrReassignedLocalVarsChecker {

  @Nullable
  public static Boolean isReassignedVar(@NotNull final GrReferenceExpression refExpr) {
    if (!PsiUtil.isCompileStatic(refExpr)) {
      return false;
    }

    if (refExpr.getQualifier() != null) {
      return false;
    }

    final PsiElement resolved = refExpr.resolve();
    if (!PsiUtil.isLocalVariable(resolved)) {
      return false;
    }

    assert resolved instanceof GrVariable;
    return CachedValuesManager.getCachedValue(resolved, new CachedValueProvider() {
      @Nullable
      @Override
      public Result compute() {
        return Result.create(isReassignedVarImpl((GrVariable)resolved), PsiModificationTracker.MODIFICATION_COUNT);
      }
    });
  }

  private static boolean isReassignedVarImpl(@NotNull final GrVariable resolved) {
    final GrControlFlowOwner variableScope = PsiTreeUtil.getParentOfType(resolved, GrCodeBlock.class, GroovyFile.class);
    if (variableScope == null) return false;

    final String name = resolved.getName();
    final Ref isReassigned = Ref.create(false);
    for (PsiElement scope = resolved.getParent().getNextSibling(); scope != null; scope = scope.getNextSibling()) {
      if (scope instanceof GroovyPsiElement) {
        ((GroovyPsiElement)scope).accept(new GroovyRecursiveElementVisitor() {
          @Override
          public void visitClosure(GrClosableBlock closure) {
            if (getUsedVarsInsideBlock(closure).contains(name)) {
              isReassigned.set(true);
            }
          }

          @Override
          public void visitElement(GroovyPsiElement element) {
            if (isReassigned.get()) return;
            super.visitElement(element);
          }
        });

        if (isReassigned.get()) break;
      }
    }

    return isReassigned.get();
  }


  @Nullable
  public static PsiType getReassignedVarType(GrReferenceExpression refExpr, boolean honorCompileStatic) {
    if (honorCompileStatic && !PsiUtil.isCompileStatic(refExpr) || refExpr.getQualifier() != null) {
      return null;
    }

    final PsiElement resolved = refExpr.resolve();
    if (!PsiUtil.isLocalVariable(resolved)) {
      return null;
    }

    assert resolved instanceof GrVariable;

    return TypeInferenceHelper.getCurrentContext().getExpressionType(((GrVariable)resolved), new Function() {
      @Override
      public PsiType fun(GrVariable variable) {
        return getLeastUpperBoundByVar(variable);
      }
    });
  }

  @Nullable
  private static PsiType getLeastUpperBoundByVar(@NotNull final GrVariable var) {
    return RecursionManager.doPreventingRecursion(var, false, new NullableComputable() {
      @Override
      public PsiType compute() {
        final Collection all = ReferencesSearch.search(var, var.getUseScope()).findAll();
        final GrExpression initializer = var.getInitializerGroovy();

        if (initializer == null && all.isEmpty()) {
          return var.getDeclaredType();
        }

        PsiType result = initializer != null ? initializer.getType() : null;

        final PsiManager manager = var.getManager();
        for (PsiReference reference : all) {
          final PsiElement ref = reference.getElement();
          if (ref instanceof GrReferenceExpression && PsiUtil.isLValue(((GrReferenceExpression)ref))) {
            result = TypesUtil.getLeastUpperBoundNullable(result, TypeInferenceHelper.getInitializerTypeFor(ref), manager);
          }
        }

        return result;
      }
    });
  }

  @NotNull
  private static Set getUsedVarsInsideBlock(@NotNull final GrCodeBlock block) {
      return CachedValuesManager.getCachedValue(block, new CachedValueProvider>() {
        @Nullable
        @Override
        public Result> compute() {
          final Set result = ContainerUtil.newHashSet();

          block.acceptChildren(new GroovyRecursiveElementVisitor() {

            @Override
            public void visitOpenBlock(GrOpenBlock openBlock) {
              result.addAll(getUsedVarsInsideBlock(openBlock));
            }

            @Override
            public void visitClosure(GrClosableBlock closure) {
              result.addAll(getUsedVarsInsideBlock(closure));
            }

            @Override
            public void visitReferenceExpression(GrReferenceExpression referenceExpression) {
              if (referenceExpression.getQualifier() == null && referenceExpression.getReferenceName() != null) {
                result.add(referenceExpression.getReferenceName());
              }
            }
          });
          return Result.create(result, block);
        }
      });
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy