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

com.jetbrains.python.inspections.PyUnboundLocalVariableInspection Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition python-community 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 com.jetbrains.python.inspections;

import com.intellij.codeInsight.controlflow.ControlFlow;
import com.intellij.codeInsight.controlflow.ControlFlowUtil;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.codeInsight.dataflow.DFALimitExceededException;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.HashSet;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.inspections.quickfix.AddGlobalQuickFix;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyGlobalStatementNavigator;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

import java.util.Set;

/**
 * @author oleg
 */
public class PyUnboundLocalVariableInspection extends PyInspection {
  private static Key> LARGE_FUNCTIONS_KEY = Key.create("PyUnboundLocalVariableInspection.LargeFunctions");

  @NotNull
  @Nls
  public String getDisplayName() {
    return PyBundle.message("INSP.NAME.unbound");
  }

  @NotNull
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull final LocalInspectionToolSession session) {
    session.putUserData(LARGE_FUNCTIONS_KEY, new HashSet());
    return new Visitor(holder, session);
  }

  public static class Visitor extends PyInspectionVisitor {

    public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) {
      super(holder, session);
    }
    @Override
    public void visitPyReferenceExpression(final PyReferenceExpression node) {
      if (node.getContainingFile() instanceof PyExpressionCodeFragment) {
        return;
      }
      // Ignore global statements arguments
      if (PyGlobalStatementNavigator.getByArgument(node) != null) {
        return;
      }
      // Ignore qualifier inspections
      if (node.isQualified()) {
        return;
      }
      // Ignore import subelements
      if (PsiTreeUtil.getParentOfType(node, PyImportStatementBase.class) != null) {
        return;
      }
      final String name = node.getReferencedName();
      if (name == null) {
        return;
      }
      final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(node, name);
      final Set largeFunctions = getSession().getUserData(LARGE_FUNCTIONS_KEY);
      assert largeFunctions != null;
      if (owner == null || largeFunctions.contains(owner)) {
        return;
      }
      // Ignore references declared in outer scopes
      if (owner != ScopeUtil.getScopeOwner(node)) {
        return;
      }
      final Scope scope = ControlFlowCache.getScope(owner);
      // Ignore globals and if scope even doesn't contain such a declaration
      if (scope.isGlobal(name) || (!scope.containsDeclaration(name))){
        return;
      }
      // Start DFA from the assignment statement in case of augmented assignments
      final PsiElement anchor;
      final PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(node, PyAugAssignmentStatement.class);
      if (augAssignment != null && name.equals(augAssignment.getTarget().getName())) {
        anchor = augAssignment;
      }
      else {
        anchor = node;
      }
      final ScopeVariable variable;
      try {
        variable = scope.getDeclaredVariable(anchor, name);
      }
      catch (DFALimitExceededException e) {
        largeFunctions.add(owner);
        registerLargeFunction(owner);
        return;
      }
      if (variable == null) {
        if (!isFirstUnboundRead(node, owner)) {
          return;
        }
        final PsiPolyVariantReference ref = node.getReference(getResolveContext());
        if (ref == null) {
          return;
        }
        final PsiElement resolved = ref.resolve();
        final boolean isBuiltin = PyBuiltinCache.getInstance(node).isBuiltin(resolved);
        if (owner instanceof PyClass) {
          if (isBuiltin || ScopeUtil.getDeclarationScopeOwner(owner, name) != null) {
            return;
          }
        }
        if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(node)) {
          return;
        }
        if (owner instanceof PyFile) {
          if (isBuiltin) {
            return;
          }
          if (resolved != null && !PyUtil.inSameFile(node, resolved)) {
            return;
          }
          registerProblem(node, PyBundle.message("INSP.unbound.name.not.defined", name));
        }
        else {
          registerProblem(node, PyBundle.message("INSP.unbound.local.variable", node.getName()),
                          ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
                          null,
                          new AddGlobalQuickFix());
        }
      }
    }

    private static boolean isFirstUnboundRead(@NotNull PyReferenceExpression node, @NotNull ScopeOwner owner) {
      final String nodeName = node.getReferencedName();
      final Scope scope = ControlFlowCache.getScope(owner);
      final ControlFlow flow = ControlFlowCache.getControlFlow(owner);
      final Instruction[] instructions = flow.getInstructions();
      final int num = ControlFlowUtil.findInstructionNumberByElement(instructions, node);
      if (num < 0) {
        return true;
      }
      final Ref first = Ref.create(true);
      ControlFlowUtil.iteratePrev(num, instructions, new Function() {
        @Override
        public ControlFlowUtil.Operation fun(Instruction instruction) {
          if (instruction instanceof ReadWriteInstruction) {
            final ReadWriteInstruction rwInstruction = (ReadWriteInstruction)instruction;
            final String name = rwInstruction.getName();
            final PsiElement element = rwInstruction.getElement();
            if (element != null && name != null && name.equals(nodeName) && instruction.num() != num) {
              try {
                if (scope.getDeclaredVariable(element, name) == null) {
                  final ReadWriteInstruction.ACCESS access = rwInstruction.getAccess();
                  if (access.isReadAccess()) {
                    first.set(false);
                    return ControlFlowUtil.Operation.BREAK;
                  }
                }
              }
              catch (DFALimitExceededException e) {
                first.set(false);
              }
              return ControlFlowUtil.Operation.CONTINUE;
            }
          }
          return ControlFlowUtil.Operation.NEXT;
        }
      });
      return first.get();
    }

    @Override
    public void visitPyNonlocalStatement(final PyNonlocalStatement node) {
      for (PyTargetExpression var : node.getVariables()) {
        final String name = var.getName();
        final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(var, name);
        if (owner == null || owner instanceof PyFile) {
          registerProblem(var, PyBundle.message("INSP.unbound.nonlocal.variable", name),
                          ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
        }
      }
    }

    private void registerLargeFunction(ScopeOwner owner) {
      registerProblem((owner instanceof PyFunction) ? ((PyFunction)owner).getNameIdentifier() : owner,
                      PyBundle.message("INSP.unbound.function.too.large", owner.getName()),
                      ProblemHighlightType.WEAK_WARNING);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy