blob: d125190b77e9903f3656df5f532e326e97f90f6f [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.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PlatformIcons;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
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.documentation.DocStringUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.references.PyQualifiedReference;
import com.jetbrains.python.psi.impl.references.PyTargetReference;
import com.jetbrains.python.psi.impl.stubs.CustomTargetExpressionStub;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.stubs.PyClassStub;
import com.jetbrains.python.psi.stubs.PyFunctionStub;
import com.jetbrains.python.psi.stubs.PyTargetExpressionStub;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author yole
*/
public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpressionStub> implements PyTargetExpression {
QualifiedName myQualifiedName;
public PyTargetExpressionImpl(ASTNode astNode) {
super(astNode);
}
public PyTargetExpressionImpl(final PyTargetExpressionStub stub) {
super(stub, PyElementTypes.TARGET_EXPRESSION);
}
@Override
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyTargetExpression(this);
}
@Nullable
@Override
public String getName() {
final PyTargetExpressionStub stub = getStub();
if (stub != null) {
return stub.getName();
}
ASTNode node = getNameElement();
return node != null ? node.getText() : null;
}
@Override
public int getTextOffset() {
final ASTNode nameElement = getNameElement();
return nameElement != null ? nameElement.getStartOffset() : getTextRange().getStartOffset();
}
@Nullable
public ASTNode getNameElement() {
return getNode().findChildByType(PyTokenTypes.IDENTIFIER);
}
public PsiElement getNameIdentifier() {
final ASTNode nameElement = getNameElement();
return nameElement == null ? null : nameElement.getPsi();
}
public String getReferencedName() {
return getName();
}
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
final ASTNode oldNameElement = getNameElement();
if (oldNameElement != null) {
final ASTNode nameElement = PyUtil.createNewName(this, name);
getNode().replaceChild(oldNameElement, nameElement);
}
return this;
}
public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
return getTypeWithAnchor(context, null);
}
@Nullable
public PyType getTypeWithAnchor(TypeEvalContext context, @Nullable PsiElement anchor) {
if (!TypeEvalStack.mayEvaluate(this)) {
return null;
}
try {
if (PyNames.ALL.equals(getName())) {
// no type for __all__, to avoid unresolved reference errors for expressions where a qualifier is a name
// imported via __all__
return null;
}
final PyType pyType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(this, context, anchor);
if (pyType != null) {
return pyType;
}
PyType type = getTypeFromDocString();
if (type != null) {
return type;
}
if (!context.maySwitchToAST(this)) {
final PsiElement value = getStub() != null ? findAssignedValueByStub(context) : findAssignedValue();
if (value instanceof PyTypedElement) {
type = context.getType((PyTypedElement)value);
if (type instanceof PyNoneType) {
return null;
}
if (type instanceof PyFunctionType) {
return type;
}
// We are unsure about the type since it may be inferred from the stub based on incomplete information
return PyUnionType.createWeakType(type);
}
return null;
}
type = getTypeFromComment(this);
if (type != null) {
return type;
}
final PsiElement parent = getParent();
if (parent instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignmentStatement = (PyAssignmentStatement)parent;
PyExpression assignedValue = assignmentStatement.getAssignedValue();
if (assignedValue instanceof PyParenthesizedExpression) {
assignedValue = ((PyParenthesizedExpression)assignedValue).getContainedExpression();
}
if (assignedValue != null) {
if (assignedValue instanceof PyReferenceExpressionImpl) {
final PyReferenceExpressionImpl refex = (PyReferenceExpressionImpl)assignedValue;
PyType maybe_type = PyUtil.getSpecialAttributeType(refex, context);
if (maybe_type != null) return maybe_type;
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
final ResolveResult[] resolveResult = refex.getReference(resolveContext).multiResolve(false);
if (resolveResult.length == 1) {
PsiElement target = resolveResult[0].getElement();
if (target == this || target == null) {
return null; // fix SOE on "a = a"
}
final PyType typeFromTarget = PyReferenceExpressionImpl.getTypeFromTarget(target, context, refex);
if (target instanceof PyTargetExpression && typeFromTarget instanceof PyNoneType) {
// this usually means that the variable is initialized to a non-None value somewhere else where we haven't looked
return null;
}
Ref<PyType> typeOfProperty = refex.getTypeOfProperty(context);
if (typeOfProperty != null) {
return typeOfProperty.get();
}
final PyType cfgType = refex.getQualifiedReferenceTypeByControlFlow(context);
if (cfgType != null) {
return cfgType;
}
return typeFromTarget;
}
}
if (assignedValue instanceof PyYieldExpression) {
return null;
}
return context.getType(assignedValue);
}
}
if (parent instanceof PyTupleExpression) {
PsiElement nextParent = parent.getParent();
while (nextParent instanceof PyParenthesizedExpression) {
nextParent = nextParent.getParent();
}
if (nextParent instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignment = (PyAssignmentStatement)nextParent;
final PyExpression value = assignment.getAssignedValue();
if (value != null) {
final PyType assignedType = PyTypeChecker.toNonWeakType(context.getType(value), context);
if (assignedType instanceof PyTupleType) {
final PyType t = getTypeFromTupleAssignment((PyTupleExpression)parent, (PyTupleType)assignedType);
if (t != null) {
return t;
}
}
}
}
}
if (parent instanceof PyWithItem) {
return getWithItemVariableType(context, (PyWithItem)parent);
}
PyType iterType = getTypeFromIteration(context);
if (iterType != null) {
return iterType;
}
PyType excType = getTypeFromExcept();
if (excType != null) {
return excType;
}
return null;
}
finally {
TypeEvalStack.evaluated(this);
}
}
@Nullable
private static PyType getWithItemVariableType(TypeEvalContext context, PyWithItem item) {
final PyExpression expression = item.getExpression();
if (expression != null) {
final PyType exprType = context.getType(expression);
if (exprType instanceof PyClassType) {
final PyClass cls = ((PyClassType)exprType).getPyClass();
final PyFunction enter = cls.findMethodByName(PyNames.ENTER, true);
if (enter != null) {
final PyType enterType = enter.getCallType(expression, Collections.<PyExpression, PyNamedParameter>emptyMap(), context);
if (enterType != null) {
return enterType;
}
for (PyTypeProvider provider: Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
PyType typeFromProvider = provider.getContextManagerVariableType(cls, expression, context);
if (typeFromProvider != null) {
return typeFromProvider;
}
}
// Guess the return type of __enter__
return PyUnionType.createWeakType(exprType);
}
}
}
return null;
}
@Nullable
public PyType getTypeFromDocString() {
String typeName = null;
final String name = getName();
final StructuredDocString targetDocString = getStructuredDocString();
if (targetDocString != null) {
typeName = targetDocString.getParamType(null);
if (typeName == null) {
typeName = targetDocString.getParamType(name);
}
}
if (typeName == null && PyUtil.isAttribute(this)) {
final PyClass cls = getContainingClass();
if (cls != null) {
final StructuredDocString classDocString = cls.getStructuredDocString();
if (classDocString != null) {
typeName = classDocString.getParamType(name);
}
}
}
if (typeName != null) {
return PyTypeParser.getTypeByName(this, typeName);
}
return null;
}
@Nullable
public static PyType getTypeFromComment(PyTargetExpressionImpl targetExpression) {
String docComment = DocStringUtil.getAttributeDocComment(targetExpression);
if (docComment != null) {
StructuredDocString structuredDocString = DocStringUtil.parse(docComment);
if (structuredDocString != null) {
String typeName = structuredDocString.getParamType(null);
if (typeName == null) {
typeName = structuredDocString.getParamType(targetExpression.getName());
}
if (typeName != null) {
return PyTypeParser.getTypeByName(targetExpression, typeName);
}
}
}
return null;
}
@Nullable
private PyType getTypeFromTupleAssignment(@NotNull PyTupleExpression tuple, @NotNull PyTupleType tupleType) {
final int count = tupleType.getElementCount();
final PyExpression[] elements = tuple.getElements();
if (elements.length == count) {
final int index = ArrayUtil.indexOf(elements, this);
if (index >= 0) {
return tupleType.getElementType(index);
}
for (int i = 0; i < count; i++) {
PyExpression element = elements[i];
while (element instanceof PyParenthesizedExpression) {
element = ((PyParenthesizedExpression)element).getContainedExpression();
}
if (element instanceof PyTupleExpression) {
final PyType elementType = tupleType.getElementType(i);
if (elementType instanceof PyTupleType) {
final PyType result = getTypeFromTupleAssignment((PyTupleExpression)element, (PyTupleType)elementType);
if (result != null) {
return result;
}
}
}
}
}
return null;
}
@Nullable
private PyType getTypeFromIteration(@NotNull TypeEvalContext context) {
PyExpression target = null;
PyExpression source = null;
final PyForPart forPart = PsiTreeUtil.getParentOfType(this, PyForPart.class);
if (forPart != null) {
final PyExpression expr = forPart.getTarget();
if (PsiTreeUtil.isAncestor(expr, this, false)) {
target = expr;
source = forPart.getSource();
}
}
final PyComprehensionElement comprh = PsiTreeUtil.getParentOfType(this, PyComprehensionElement.class);
if (comprh != null) {
for (ComprhForComponent c : comprh.getForComponents()) {
final PyExpression expr = c.getIteratorVariable();
if (PsiTreeUtil.isAncestor(expr, this, false)) {
target = expr;
source = c.getIteratedList();
}
}
}
if (source != null) {
final PyType sourceType = context.getType(source);
final PyType type = getIterationType(sourceType, source, this, context);
if (type instanceof PyTupleType && target instanceof PyTupleExpression) {
return getTypeFromTupleAssignment((PyTupleExpression)target, (PyTupleType)type);
}
if (target == this && type != null) {
return type;
}
}
return null;
}
@Nullable
private static PyType getIterationType(@Nullable PyType iterableType, @Nullable PyExpression source, @NotNull PsiElement anchor,
@NotNull TypeEvalContext context) {
PyType result = null;
if (iterableType instanceof PyCollectionType) {
result = ((PyCollectionType)iterableType).getElementType(context);
if (iterableType instanceof PyClassType) {
final PyClass cls = ((PyClassType)iterableType).getPyClass();
if (result instanceof PyTupleType && PyABCUtil.isSubclass(cls, PyNames.MAPPING)) {
final PyTupleType mappingType = (PyTupleType)result;
if (mappingType.getElementCount() == 2) {
result = mappingType.getElementType(0);
}
}
}
}
else if (iterableType instanceof PyTupleType) {
final PyTupleType tupleType = (PyTupleType)iterableType;
final List<PyType> memberTypes = new ArrayList<PyType>();
for (int i = 0; i < tupleType.getElementCount(); i++) {
memberTypes.add(tupleType.getElementType(i));
}
return PyUnionType.union(memberTypes);
}
else if (iterableType instanceof PyUnionType) {
final Collection<PyType> members = ((PyUnionType)iterableType).getMembers();
final List<PyType> iterationTypes = new ArrayList<PyType>();
for (PyType member : members) {
iterationTypes.add(getIterationType(member, source, anchor, context));
}
return PyUnionType.union(iterationTypes);
}
else if (iterableType != null && PyABCUtil.isSubtype(iterableType, PyNames.ITERATOR, context)) {
final PyFunction iterateMethod = findMethodByName(iterableType, PyNames.ITER, context);
PyType iterateMethodType = null;
if (iterateMethod != null) {
iterateMethodType = getContextSensitiveType(iterateMethod, context, source);
}
if (iterateMethodType instanceof PyCollectionType) {
final PyCollectionType collectionType = (PyCollectionType)iterateMethodType;
result = collectionType.getElementType(context);
}
if (result == null) {
final String nextMethodName = LanguageLevel.forElement(anchor).isAtLeast(LanguageLevel.PYTHON30) ?
PyNames.DUNDER_NEXT : PyNames.NEXT;
final PyFunction next = findMethodByName(iterableType, nextMethodName, context);
if (next != null) {
result = getContextSensitiveType(next, context, source);
}
}
if (result == null) {
final PyFunction getItem = findMethodByName(iterableType, PyNames.GETITEM, context);
if (getItem != null) {
result = getContextSensitiveType(getItem, context, source);
}
}
}
return result;
}
@Nullable
private static PyFunction findMethodByName(@NotNull PyType type, @NotNull String name, @NotNull TypeEvalContext context) {
final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(context);
final List<? extends RatedResolveResult> results = type.resolveMember(name, null, AccessDirection.READ, resolveContext);
if (results != null && !results.isEmpty()) {
final RatedResolveResult result = results.get(0);
final PsiElement element = result.getElement();
if (element instanceof PyFunction) {
return (PyFunction)element;
}
}
return null;
}
@Nullable
private static PyType getContextSensitiveType(@NotNull PyFunction function, @NotNull TypeEvalContext context,
@Nullable PyExpression source) {
return function.getCallType(source, Collections.<PyExpression, PyNamedParameter>emptyMap(), context);
}
@Nullable
private PyType getTypeFromExcept() {
PyExceptPart exceptPart = PsiTreeUtil.getParentOfType(this, PyExceptPart.class);
if (exceptPart == null || exceptPart.getTarget() != this) {
return null;
}
final PyExpression exceptClass = exceptPart.getExceptClass();
if (exceptClass instanceof PyReferenceExpression) {
final PsiElement element = ((PyReferenceExpression)exceptClass).getReference().resolve();
if (element instanceof PyClass) {
return new PyClassTypeImpl((PyClass) element, false);
}
}
return null;
}
public PyExpression getQualifier() {
ASTNode qualifier = getNode().findChildByType(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens());
return qualifier != null ? (PyExpression) qualifier.getPsi() : null;
}
@Nullable
@Override
public QualifiedName asQualifiedName() {
if (myQualifiedName == null) {
myQualifiedName = PyPsiUtils.asQualifiedName(this);
}
return myQualifiedName;
}
public String toString() {
return super.toString() + ": " + getName();
}
public Icon getIcon(final int flags) {
if (isQualified() || PsiTreeUtil.getStubOrPsiParentOfType(this, PyDocStringOwner.class) instanceof PyClass) {
return PlatformIcons.FIELD_ICON;
}
return PlatformIcons.VARIABLE_ICON;
}
public boolean isQualified() {
PyTargetExpressionStub stub = getStub();
if (stub != null) {
return stub.isQualified();
}
return getQualifier() != null;
}
@Nullable
public PyExpression findAssignedValue() {
if (isValid()) {
PyAssignmentStatement assignment = PsiTreeUtil.getParentOfType(this, PyAssignmentStatement.class);
if (assignment != null) {
List<Pair<PyExpression, PyExpression>> mapping = assignment.getTargetsToValuesMapping();
for (Pair<PyExpression, PyExpression> pair : mapping) {
PyExpression assigned_to = pair.getFirst();
if (assigned_to == this) return pair.getSecond();
}
}
}
return null;
}
public QualifiedName getAssignedQName() {
final PyTargetExpressionStub stub = getStub();
if (stub != null) {
if (stub.getInitializerType() == PyTargetExpressionStub.InitializerType.ReferenceExpression) {
return stub.getInitializer();
}
return null;
}
return PyPsiUtils.asQualifiedName(findAssignedValue());
}
@Nullable
public PsiElement findAssignedValueByStub(@NotNull TypeEvalContext context) {
final PyTargetExpressionStub stub = getStub();
if (stub != null && stub.getInitializerType() == PyTargetExpressionStub.InitializerType.ReferenceExpression) {
final QualifiedName initializer = stub.getInitializer();
// TODO: Support qualified stub initializers
if (initializer != null && initializer.getComponentCount() == 1) {
final String name = initializer.getLastComponent();
if (name != null) {
final PsiElement parent = getParentByStub();
if (parent instanceof PyFile) {
return ((PyFile)parent).getElementNamed(name);
}
else if (parent instanceof PyClass) {
final PyType type = context.getType((PyClass)parent);
if (type != null) {
final List<? extends RatedResolveResult> results = type.resolveMember(name, null, AccessDirection.READ,
PyResolveContext.noImplicits());
if (results != null && !results.isEmpty()) {
return results.get(0).getElement();
}
}
}
}
}
}
return null;
}
@Override
public QualifiedName getCalleeName() {
final PyTargetExpressionStub stub = getStub();
if (stub != null) {
final PyTargetExpressionStub.InitializerType initializerType = stub.getInitializerType();
if (initializerType == PyTargetExpressionStub.InitializerType.CallExpression) {
return stub.getInitializer();
}
else if (initializerType == PyTargetExpressionStub.InitializerType.Custom) {
final CustomTargetExpressionStub customStub = stub.getCustomStub(CustomTargetExpressionStub.class);
if (customStub != null) {
return customStub.getCalleeName();
}
}
return null;
}
final PyExpression value = findAssignedValue();
if (value instanceof PyCallExpression) {
final PyExpression callee = ((PyCallExpression)value).getCallee();
return PyPsiUtils.asQualifiedName(callee);
}
return null;
}
@NotNull
@Override
public PsiReference getReference() {
return getReference(PyResolveContext.defaultContext());
}
@NotNull
public PsiPolyVariantReference getReference(final PyResolveContext resolveContext) {
if (isQualified()) {
return new PyQualifiedReference(this, resolveContext);
}
return new PyTargetReference(this, resolveContext);
}
@NotNull
@Override
public SearchScope getUseScope() {
if (isQualified()) {
return super.getUseScope();
}
final ScopeOwner owner = ScopeUtil.getScopeOwner(this);
if (owner != null) {
final Scope scope = ControlFlowCache.getScope(owner);
if (scope.isGlobal(getName())) {
return GlobalSearchScope.projectScope(getProject());
}
if (scope.isNonlocal(getName())) {
return new LocalSearchScope(getContainingFile());
}
}
// find highest level function containing our var
PyElement container = this;
while(true) {
PyElement parentContainer = PsiTreeUtil.getParentOfType(container, PyFunction.class, PyClass.class);
if (parentContainer instanceof PyClass) {
if (isQualified()) {
return super.getUseScope();
}
break;
}
if (parentContainer == null) {
break;
}
container = parentContainer;
}
if (container instanceof PyFunction) {
return new LocalSearchScope(container);
}
return super.getUseScope();
}
@Override
public PyClass getContainingClass() {
final PyTargetExpressionStub stub = getStub();
if (stub != null) {
final StubElement parentStub = stub.getParentStub();
if (parentStub instanceof PyClassStub) {
return ((PyClassStub)parentStub).getPsi();
}
if (parentStub instanceof PyFunctionStub) {
final StubElement functionParent = parentStub.getParentStub();
if (functionParent instanceof PyClassStub) {
return ((PyClassStub) functionParent).getPsi();
}
}
return null;
}
final PsiElement parent = PsiTreeUtil.getParentOfType(this, PyFunction.class, PyClass.class);
if (parent instanceof PyClass) {
return (PyClass)parent;
}
if (parent instanceof PyFunction) {
return ((PyFunction)parent).getContainingClass();
}
return null;
}
@Override
public ItemPresentation getPresentation() {
return new PyElementPresentation(this) {
@Nullable
@Override
public String getLocationString() {
final PyClass containingClass = getContainingClass();
if (containingClass != null) {
return "(" + containingClass.getName() + " in " + getPackageForFile(getContainingFile()) + ")";
}
return super.getLocationString();
}
};
}
@Nullable
@Override
public String getDocStringValue() {
final PyTargetExpressionStub stub = getStub();
if (stub != null) {
return stub.getDocString();
}
return DocStringUtil.getDocStringValue(this);
}
@Nullable
@Override
public StructuredDocString getStructuredDocString() {
return DocStringUtil.getStructuredDocString(this);
}
@Nullable
@Override
public PyStringLiteralExpression getDocStringExpression() {
final PsiElement parent = getParent();
if (parent instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignment = (PyAssignmentStatement)parent;
PsiElement nextSibling = assignment.getNextSibling();
while (nextSibling != null && (nextSibling instanceof PsiWhiteSpace || nextSibling instanceof PsiComment)) {
nextSibling = nextSibling.getNextSibling();
}
if (nextSibling instanceof PyExpressionStatement) {
final PyExpression expression = ((PyExpressionStatement)nextSibling).getExpression();
if (expression instanceof PyStringLiteralExpression) {
return (PyStringLiteralExpression)expression;
}
}
}
return null;
}
@Override
public void subtreeChanged() {
super.subtreeChanged();
myQualifiedName = null;
}
@Nullable
@Override
public String getQualifiedName() {
return QualifiedNameFinder.getQualifiedName(this);
}
}