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<Boolean> first = Ref.create(true); ControlFlowUtil.iteratePrev( num, instructions, new Function<Instruction, ControlFlowUtil.Operation>() { @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(); }
public void subtreeChanged() { super.subtreeChanged(); ControlFlowCache.clear(this); }
private void processElement(@NotNull final PsiNameIdentifierOwner element) { if (isConditional(element)) { return; } final String name = element.getName(); final ScopeOwner owner = ScopeUtil.getScopeOwner(element); if (owner != null && name != null) { final Instruction[] instructions = ControlFlowCache.getControlFlow(owner).getInstructions(); PsiElement elementInControlFlow = element; if (element instanceof PyTargetExpression) { final PyImportStatement importStatement = PsiTreeUtil.getParentOfType(element, PyImportStatement.class); if (importStatement != null) { elementInControlFlow = importStatement; } } final int startInstruction = ControlFlowUtil.findInstructionNumberByElement(instructions, elementInControlFlow); if (startInstruction < 0) { return; } final Ref<PsiElement> readElementRef = Ref.create(null); final Ref<PsiElement> writeElementRef = Ref.create(null); ControlFlowUtil.iteratePrev( startInstruction, instructions, new Function<Instruction, ControlFlowUtil.Operation>() { @Override public ControlFlowUtil.Operation fun(Instruction instruction) { if (instruction instanceof ReadWriteInstruction && instruction.num() != startInstruction) { final ReadWriteInstruction rwInstruction = (ReadWriteInstruction) instruction; if (name.equals(rwInstruction.getName())) { final PsiElement originalElement = rwInstruction.getElement(); if (originalElement != null) { if (rwInstruction.getAccess().isReadAccess()) { readElementRef.set(originalElement); } if (rwInstruction.getAccess().isWriteAccess()) { if (originalElement != element) { writeElementRef.set(originalElement); } } } return ControlFlowUtil.Operation.CONTINUE; } } return ControlFlowUtil.Operation.NEXT; } }); final PsiElement writeElement = writeElementRef.get(); if (writeElement != null && readElementRef.get() == null) { final List<LocalQuickFix> quickFixes = new ArrayList<LocalQuickFix>(); if (suggestRename(element, writeElement)) { quickFixes.add(new PyRenameElementQuickFix()); } final PsiElement identifier = element.getNameIdentifier(); registerProblem( identifier != null ? identifier : element, PyBundle.message("INSP.redeclared.name", name), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, null, quickFixes.toArray(new LocalQuickFix[quickFixes.size()])); } } }
@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.getQualifier() != null) { 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<ScopeOwner> 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(resolveWithoutImplicits()); if (ref == null) { return; } final PsiElement resolved = ref.resolve(); final boolean isBuiltin = PyBuiltinCache.getInstance(node).hasInBuiltins(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()); } } }