blob: 8f9936428cb283498b03f86b53d2ed9d763dd2c9 [file] [log] [blame]
/*
* Copyright 2000-2013 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.codeInsight.dataflow.scope;
import com.intellij.codeInsight.controlflow.ControlFlow;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.psi.PsiElement;
import com.intellij.psi.StubBasedPsiElement;
import com.intellij.psi.stubs.StubElement;
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.psi.*;
import com.jetbrains.python.psi.impl.PyExceptPartNavigator;
import com.jetbrains.python.psi.impl.PyForStatementNavigator;
import com.jetbrains.python.psi.impl.PyListCompExpressionNavigator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import static com.intellij.psi.util.PsiTreeUtil.getParentOfType;
import static com.intellij.psi.util.PsiTreeUtil.isAncestor;
/**
* @author oleg
*/
public class ScopeUtil {
private ScopeUtil() {
}
@Nullable
public static PsiElement getParameterScope(final PsiElement element){
if (element instanceof PyNamedParameter){
final PyFunction function = getParentOfType(element, PyFunction.class, false);
if (function != null){
return function;
}
}
final PyExceptPart exceptPart = PyExceptPartNavigator.getPyExceptPartByTarget(element);
if (exceptPart != null){
return exceptPart;
}
final PyForStatement forStatement = PyForStatementNavigator.getPyForStatementByIterable(element);
if (forStatement != null){
return forStatement;
}
final PyListCompExpression listCompExpression = PyListCompExpressionNavigator.getPyListCompExpressionByVariable(element);
if (listCompExpression != null){
return listCompExpression;
}
return null;
}
/**
* Return the scope owner for the element.
*
* Scope owner is not always the first ScopeOwner parent of the element. Some elements are resolved in outer scopes.
*/
@Nullable
public static ScopeOwner getScopeOwner(@Nullable PsiElement element) {
if (element instanceof StubBasedPsiElement) {
final StubElement stub = ((StubBasedPsiElement)element).getStub();
if (stub != null) {
StubElement parentStub = stub.getParentStub();
while (parentStub != null) {
final PsiElement parent = parentStub.getPsi();
if (parent instanceof ScopeOwner) {
return (ScopeOwner)parent;
}
parentStub = parentStub.getParentStub();
}
return null;
}
}
final ScopeOwner firstOwner = getParentOfType(element, ScopeOwner.class);
if (firstOwner == null) {
return null;
}
final ScopeOwner nextOwner = getParentOfType(firstOwner, ScopeOwner.class);
// References in decorator expressions are resolved outside of the function (if the lambda is not inside the decorator)
final PyElement decoratorAncestor = getParentOfType(element, PyDecorator.class);
if (decoratorAncestor != null && !isAncestor(decoratorAncestor, firstOwner, true)) {
return nextOwner;
}
// References in default values of parameters are resolved outside of the function (if the lambda is not inside the default value)
final PyParameter parameterAncestor = getParentOfType(element, PyParameter.class);
if (parameterAncestor != null && !isAncestor(parameterAncestor, firstOwner, true)) {
final PyExpression defaultValue = parameterAncestor.getDefaultValue();
if (element != null && isAncestor(defaultValue, element, false)) {
return nextOwner;
}
}
// Superclasses are resolved outside of the class
final PyClass containingClass = getParentOfType(element, PyClass.class);
if (containingClass != null && element != null &&
isAncestor(containingClass.getSuperClassExpressionList(), element, false)) {
return nextOwner;
}
return firstOwner;
}
@Nullable
public static ScopeOwner getDeclarationScopeOwner(PsiElement anchor, String name) {
PsiElement element = anchor;
if (name != null) {
final ScopeOwner originalScopeOwner = getScopeOwner(element);
ScopeOwner scopeOwner = originalScopeOwner;
while (scopeOwner != null) {
if (!(scopeOwner instanceof PyClass) || scopeOwner == originalScopeOwner) {
Scope scope = ControlFlowCache.getScope(scopeOwner);
if (scope.containsDeclaration(name)) {
return scopeOwner;
}
}
scopeOwner = getScopeOwner(scopeOwner);
}
}
return null;
}
@NotNull
public static Collection<PsiElement> getReadWriteElements(@NotNull String name, @NotNull ScopeOwner scopeOwner, boolean isReadAccess,
boolean isWriteAccess) {
ControlFlow flow = ControlFlowCache.getControlFlow(scopeOwner);
Collection<PsiElement> result = new ArrayList<PsiElement>();
for (Instruction instr : flow.getInstructions()) {
if (instr instanceof ReadWriteInstruction) {
ReadWriteInstruction rw = (ReadWriteInstruction)instr;
if (name.equals(rw.getName())) {
ReadWriteInstruction.ACCESS access = rw.getAccess();
if ((isReadAccess && access.isReadAccess()) || (isWriteAccess && access.isWriteAccess())) {
result.add(rw.getElement());
}
}
}
}
return result;
}
}