private void checkExpression(PyQualifiedExpression node) { final PyExpression qualifier = node.getQualifier(); if (qualifier != null) { final PyType type = myTypeEvalContext.getType(qualifier); if (type instanceof PyClassType) { final PyClass cls = ((PyClassType) type).getPyClass(); final String name = node.getName(); if (name != null) { final Pair<PyClass, String> key = new Pair<PyClass, String>(cls, name); final Property property; if (myPropertyCache.containsKey(key)) { property = myPropertyCache.get(key); } else { property = cls.findProperty(name, true); } myPropertyCache.put( key, property); // we store nulls, too, to know that a property does not exist if (property != null) { final AccessDirection dir = AccessDirection.of(node); checkAccessor(node, name, dir, property); if (dir == AccessDirection.READ) { final PsiElement parent = node.getParent(); if (parent instanceof PyAugAssignmentStatement && ((PyAugAssignmentStatement) parent).getTarget() == node) { checkAccessor(node, name, AccessDirection.WRITE, property); } } } } } } }
@Nullable private Ref<PyType> getTypeOfProperty( @Nullable PyType qualifierType, @NotNull String name, @NotNull TypeEvalContext context) { if (qualifierType instanceof PyClassType) { final PyClassType classType = (PyClassType) qualifierType; PyClass pyClass = classType.getPyClass(); Property property = pyClass.findProperty(name, true); if (property != null) { if (classType.isDefinition()) { return Ref.<PyType>create( PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY)); } if (AccessDirection.of(this) == AccessDirection.READ) { final PyType type = property.getType(context); if (type != null) { return Ref.create(type); } } return Ref.create(); } } else if (qualifierType instanceof PyUnionType) { final PyUnionType unionType = (PyUnionType) qualifierType; for (PyType type : unionType.getMembers()) { final Ref<PyType> result = getTypeOfProperty(type, name, context); if (result != null) { return result; } } } return null; }
public Property getProperty() { final PyClass containingClass = getContainingClass(); if (containingClass != null) { return containingClass.findPropertyByCallable(this); } return null; }
@NotNull private static List<PyGenericType> collectGenericTypes( @NotNull PyClass cls, @NotNull Context context) { boolean isGeneric = false; for (PyClass ancestor : cls.getAncestorClasses(context.getTypeContext())) { if (GENERIC_CLASSES.contains(ancestor.getQualifiedName())) { isGeneric = true; break; } } if (isGeneric) { final ArrayList<PyGenericType> results = new ArrayList<>(); // XXX: Requires switching from stub to AST for (PyExpression expr : cls.getSuperClassExpressions()) { if (expr instanceof PySubscriptionExpression) { final PyExpression indexExpr = ((PySubscriptionExpression) expr).getIndexExpression(); if (indexExpr != null) { for (PsiElement resolved : tryResolving(indexExpr, context.getTypeContext())) { final PyGenericType genericType = getGenericType(resolved, context); if (genericType != null) { results.add(genericType); } } } } } return results; } return Collections.emptyList(); }
/** * Creates abstract version of each method in each class (does not touch method itself as opposite * to {@link #moveMethods(com.jetbrains.python.psi.PyClass, java.util.Collection, * com.jetbrains.python.psi.PyClass...)}) * * @param currentFunctions functions to make them abstract * @param to classes where abstract method should be created */ private static void makeMethodsAbstract( final Collection<PyFunction> currentFunctions, final PyClass... to) { final Set<PsiFile> filesToCheckImport = new HashSet<PsiFile>(); final Set<PyClass> classesToAddMetaAbc = new HashSet<PyClass>(); for (final PyFunction function : currentFunctions) { for (final PyClass destClass : to) { final PyFunctionBuilder functionBuilder = PyFunctionBuilder.copySignature(function, DECORATORS_MAY_BE_COPIED_TO_ABSTRACT); functionBuilder.decorate(PyNames.ABSTRACTMETHOD); final LanguageLevel level = LanguageLevel.forElement(destClass); PyClassRefactoringUtil.addMethods( destClass, false, functionBuilder.buildFunction(destClass.getProject(), level)); classesToAddMetaAbc.add(destClass); } } // Add ABCMeta to new classes if needed for (final PyClass aClass : classesToAddMetaAbc) { if (addMetaAbcIfNeeded(aClass)) { filesToCheckImport.add(aClass.getContainingFile()); } } // Add imports for ABC if needed for (final PsiFile file : filesToCheckImport) { addImportFromAbc(file, PyNames.ABSTRACTMETHOD); addImportFromAbc(file, ABC_META_CLASS); PyClassRefactoringUtil.optimizeImports(file); // To remove redundant imports } }
@Nullable private static PyType getParameterizedType( @NotNull PsiElement element, @NotNull Context context) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression) element; final PyExpression operand = subscriptionExpr.getOperand(); final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); final PyType operandType = getType(operand, context); if (operandType instanceof PyClassType) { final PyClass cls = ((PyClassType) operandType).getPyClass(); final List<PyType> indexTypes = getIndexTypes(subscriptionExpr, context); if (PyNames.TUPLE.equals(cls.getQualifiedName())) { if (indexExpr instanceof PyTupleExpression) { final PyExpression[] elements = ((PyTupleExpression) indexExpr).getElements(); if (elements.length == 2 && isEllipsis(elements[1])) { return PyTupleType.createHomogeneous(element, indexTypes.get(0)); } } return PyTupleType.create(element, indexTypes); } else if (indexExpr != null) { return new PyCollectionTypeImpl(cls, false, indexTypes); } } } return null; }
/** * Looks for two standard decorators to a function, or a wrapping assignment that closely follows * it. * * @return a flag describing what was detected. */ @Nullable public Modifier getModifier() { final String deconame = getClassOrStaticMethodDecorator(); if (PyNames.CLASSMETHOD.equals(deconame)) { return CLASSMETHOD; } else if (PyNames.STATICMETHOD.equals(deconame)) { return STATICMETHOD; } // implicit staticmethod __new__ final PyClass cls = getContainingClass(); if (cls != null && PyNames.NEW.equals(getName()) && cls.isNewStyleClass(null)) { return STATICMETHOD; } // if (getStub() != null) { return getWrappersFromStub(); } final String funcName = getName(); if (funcName != null) { PyAssignmentStatement currentAssignment = PsiTreeUtil.getNextSiblingOfType(this, PyAssignmentStatement.class); while (currentAssignment != null) { final String modifier = currentAssignment .getTargetsToValuesMapping() .stream() .filter( pair -> pair.getFirst() instanceof PyTargetExpression && funcName.equals(pair.getFirst().getName())) .filter(pair -> pair.getSecond() instanceof PyCallExpression) .map( pair -> interpretAsModifierWrappingCall((PyCallExpression) pair.getSecond(), this)) .filter(interpreted -> interpreted != null && interpreted.getSecond() == this) .map(interpreted -> interpreted.getFirst()) .filter( wrapperName -> PyNames.CLASSMETHOD.equals(wrapperName) || PyNames.STATICMETHOD.equals(wrapperName)) .findAny() .orElse(null); if (PyNames.CLASSMETHOD.equals(modifier)) { return CLASSMETHOD; } else if (PyNames.STATICMETHOD.equals(modifier)) { return STATICMETHOD; } currentAssignment = PsiTreeUtil.getNextSiblingOfType(currentAssignment, PyAssignmentStatement.class); } } return null; }
private static boolean isAbstractMethodForClass( @NotNull PyFunction method, @NotNull PyClass cls) { final String methodName = method.getName(); if (methodName == null || cls.findMethodByName(methodName, false) != null || cls.findClassAttribute(methodName, false) != null) { return false; } return PyUtil.isDecoratedAsAbstract(method) || PyOverrideImplementUtil.raisesNotImplementedError(method); }
protected String getElementLocation() { final PyClass containingClass = getContainingClass(); if (containingClass != null) { return "(" + containingClass.getName() + " in " + getPackageForFile(getContainingFile()) + ")"; } return super.getElementLocation(); }
private static boolean inConstructor(@NotNull PsiElement expression) { final PsiElement expr = expression instanceof PyClass ? expression : expression.getParent(); PyClass clazz = PyUtil.getContainingClassOrSelf(expr); final ScopeOwner current = ScopeUtil.getScopeOwner(expression); if (clazz != null && current != null && current instanceof PyFunction) { PyFunction init = clazz.findMethodByName(PyNames.INIT, false, null); if (current == init) { return true; } } return false; }
@Nullable private static Boolean isOverrides(final PyFunction pyFunction) { final PyClass clazz = PyUtil.getContainingClassOrSelf(pyFunction); assert clazz != null : "Refactoring called on function, not method: " + pyFunction; for (final PyClass parentClass : clazz.getSuperClasses()) { final PyFunction parentMethod = parentClass.findMethodByName(pyFunction.getName(), true); if (parentMethod != null) { return true; } } return null; }
/** * Looks for two standard decorators to a function, or a wrapping assignment that closely follows * it. * * @return a flag describing what was detected. */ @Nullable public Modifier getModifier() { String deconame = getClassOrStaticMethodDecorator(); if (PyNames.CLASSMETHOD.equals(deconame)) { return CLASSMETHOD; } else if (PyNames.STATICMETHOD.equals(deconame)) { return STATICMETHOD; } // implicit staticmethod __new__ PyClass cls = getContainingClass(); if (cls != null && PyNames.NEW.equals(getName()) && cls.isNewStyleClass()) { return STATICMETHOD; } // if (getStub() != null) { return getWrappersFromStub(); } String func_name = getName(); if (func_name != null) { PyAssignmentStatement assignment = PsiTreeUtil.getNextSiblingOfType(this, PyAssignmentStatement.class); if (assignment != null) { for (Pair<PyExpression, PyExpression> pair : assignment.getTargetsToValuesMapping()) { PyExpression value = pair.getSecond(); if (value instanceof PyCallExpression) { PyExpression target = pair.getFirst(); if (target instanceof PyTargetExpression && func_name.equals(target.getName())) { Pair<String, PyFunction> interpreted = interpretAsModifierWrappingCall((PyCallExpression) value, this); if (interpreted != null) { PyFunction original = interpreted.getSecond(); if (original == this) { String wrapper_name = interpreted.getFirst(); if (PyNames.CLASSMETHOD.equals(wrapper_name)) { return CLASSMETHOD; } else if (PyNames.STATICMETHOD.equals(wrapper_name)) { return STATICMETHOD; } } } } } } } } return null; }
@Nullable private static PsiElement addFieldToSetUp( PyClass clazz, final Function<String, PyStatement> callback) { final PyFunction init = clazz.findMethodByName(PythonUnitTestUtil.TESTCASE_SETUP_NAME, false, null); if (init != null) { return AddFieldQuickFix.appendToMethod(init, callback); } final PyFunctionBuilder builder = new PyFunctionBuilder(PythonUnitTestUtil.TESTCASE_SETUP_NAME, clazz); builder.parameter(PyNames.CANONICAL_SELF); PyFunction setUp = builder.buildFunction(clazz.getProject(), LanguageLevel.getDefault()); final PyStatementList statements = clazz.getStatementList(); final PsiElement anchor = statements.getFirstChild(); setUp = (PyFunction) statements.addBefore(setUp, anchor); return AddFieldQuickFix.appendToMethod(setUp, callback); }
private void addPredefinedMethodDoc(PyFunction fun, String mothodName) { PyClassType objectType = PyBuiltinCache.getInstance(fun) .getObjectType(); // old- and new-style classes share the __xxx__ stuff if (objectType != null) { PyClass objectClass = objectType.getPyClass(); PyFunction predefinedMethod = objectClass.findMethodByName(mothodName, false); if (predefinedMethod != null) { PyStringLiteralExpression predefinedDocstring = predefinedMethod.getDocStringExpression(); String predefinedDoc = predefinedDocstring != null ? predefinedDocstring.getStringValue() : null; if (predefinedDoc != null && predefinedDoc.length() > 1) { // only a real-looking doc string counts addFormattedDocString(fun, predefinedDoc, myBody, myBody); myEpilog.addItem(BR).addItem(BR).addItem(PyBundle.message("QDOC.copied.from.builtin")); } } } }
private void buildFromAttributeDoc() { PyClass cls = PsiTreeUtil.getParentOfType(myElement, PyClass.class); assert cls != null; String type = PyUtil.isInstanceAttribute((PyExpression) myElement) ? "Instance attribute " : "Class attribute "; myProlog .addItem(type) .addWith(TagBold, $().addWith(TagCode, $(((PyTargetExpression) myElement).getName()))) .addItem(" of class ") .addWith(PythonDocumentationProvider.LinkMyClass, $().addWith(TagCode, $(cls.getName()))) .addItem(BR); final String docString = ((PyTargetExpression) myElement).getDocStringValue(); if (docString != null) { addFormattedDocString(myElement, docString, myBody, myEpilog); } }
@Nullable @Override public String getQualifiedName() { String name = getName(); if (name == null) { return null; } PyClass containingClass = getContainingClass(); if (containingClass != null) { return containingClass.getQualifiedName() + "." + name; } if (PsiTreeUtil.getStubOrPsiParent(this) instanceof PyFile) { VirtualFile virtualFile = getContainingFile().getVirtualFile(); if (virtualFile != null) { final String packageName = QualifiedNameFinder.findShortestImportableName(this, virtualFile); return packageName + "." + name; } } return null; }
@Override public void visitPyClass(PyClass pyClass) { if (isAbstract(pyClass)) { return; } final Set<PyFunction> toBeImplemented = new HashSet<PyFunction>(); final Collection<PyFunction> functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass); for (PyFunction method : functions) { if (isAbstractMethodForClass(method, pyClass)) { toBeImplemented.add(method); } } final ASTNode nameNode = pyClass.getNameNode(); if (!toBeImplemented.isEmpty() && nameNode != null) { registerProblem( nameNode.getPsi(), PyBundle.message("INSP.NAME.abstract.class.$0.must.implement", pyClass.getName()), new PyImplementMethodsQuickFix(pyClass, toBeImplemented)); } }
private static boolean isAbstract(@NotNull PyClass pyClass) { final PyType metaClass = pyClass.getMetaClassType( TypeEvalContext.userInitiated(pyClass.getProject(), pyClass.getContainingFile())); if (metaClass instanceof PyClassLikeType && PyNames.ABC_META_CLASS.equals(metaClass.getName())) { return true; } if (metaClass == null) { final PyExpression metaClassExpr = as(pyClass.getMetaClassExpression(), PyReferenceExpression.class); if (metaClassExpr != null && PyNames.ABC_META_CLASS.equals(metaClassExpr.getName())) { return true; } } for (PyFunction method : pyClass.getMethods(false)) { if (PyUtil.isDecoratedAsAbstract(method)) { return true; } } return false; }
// TODO: Copy/Paste with PyClass.getMeta.. private static boolean addMetaAbcIfNeeded(@NotNull final PyClass aClass) { final PsiFile file = aClass.getContainingFile(); final PyType type = aClass.getMetaClassType(TypeEvalContext.userInitiated(file)); if (type != null) { return false; // User already has metaclass. He probably knows about metaclasses, so we should // not add ABCMeta } final LanguageLevel languageLevel = LanguageLevel.forElement(aClass); if (languageLevel .isPy3K()) { // TODO: Copy/paste, use strategy because we already has the same check in // #couldBeAbstract // Add (metaclass= for Py3K PyClassRefactoringUtil.addSuperClassExpressions( aClass.getProject(), aClass, null, Collections.singletonList(Pair.create(PyNames.METACLASS, ABC_META_CLASS))); } else { // Add __metaclass__ for Py2 PyClassRefactoringUtil.addClassAttributeIfNotExist( aClass, PyNames.DUNDER_METACLASS, ABC_META_CLASS); } return true; }
private boolean buildFromProperty( PsiElement elementDefinition, @Nullable final PsiElement outerElement, @NotNull final TypeEvalContext context) { if (myOriginalElement == null) { return false; } final String elementName = myOriginalElement.getText(); if (!PyNames.isIdentifier(elementName)) { return false; } if (!(outerElement instanceof PyQualifiedExpression)) { return false; } final PyExpression qualifier = ((PyQualifiedExpression) outerElement).getQualifier(); if (qualifier == null) { return false; } final PyType type = context.getType(qualifier); if (!(type instanceof PyClassType)) { return false; } final PyClass cls = ((PyClassType) type).getPyClass(); final Property property = cls.findProperty(elementName, true, null); if (property == null) { return false; } final AccessDirection direction = AccessDirection.of((PyElement) outerElement); final Maybe<PyCallable> accessor = property.getByDirection(direction); myProlog .addItem("property ") .addWith(TagBold, $().addWith(TagCode, $(elementName))) .addItem(" of ") .add(PythonDocumentationProvider.describeClass(cls, TagCode, true, true)); if (accessor.isDefined() && property.getDoc() != null) { myBody.addItem(": ").addItem(property.getDoc()).addItem(BR); } else { final PyCallable getter = property.getGetter().valueOrNull(); if (getter != null && getter != myElement && getter instanceof PyFunction) { // not in getter, getter's doc comment may be useful final PyStringLiteralExpression docstring = ((PyFunction) getter).getDocStringExpression(); if (docstring != null) { myProlog .addItem(BR) .addWith(TagItalic, $("Copied from getter:")) .addItem(BR) .addItem(docstring.getStringValue()); } } myBody.addItem(BR); } myBody.addItem(BR); if (accessor.isDefined() && accessor.value() == null) elementDefinition = null; final String accessorKind = getAccessorKind(direction); if (elementDefinition != null) { myEpilog.addWith(TagSmall, $(BR, BR, accessorKind, " of property")).addItem(BR); } if (!(elementDefinition instanceof PyDocStringOwner)) { myBody .addWith( TagItalic, elementDefinition != null ? $("Declaration: ") : $(accessorKind + " is not defined.")) .addItem(BR); if (elementDefinition != null) { myBody.addItem(combUp(PyUtil.getReadableRepr(elementDefinition, false))); } } return true; }
private void addInheritedDocString( @NotNull final PyFunction pyFunction, @Nullable final PyClass pyClass) { boolean notFound = true; final String methodName = pyFunction.getName(); if (pyClass == null || methodName == null) { return; } final boolean isConstructor = PyNames.INIT.equals(methodName); Iterable<PyClass> classes = pyClass.getAncestorClasses(null); if (isConstructor) { // look at our own class again and maybe inherit class's doc classes = new ChainIterable<PyClass>(pyClass).add(classes); } for (PyClass ancestor : classes) { PyStringLiteralExpression docstringElement = null; PyFunction inherited = null; boolean isFromClass = false; if (isConstructor) docstringElement = pyClass.getDocStringExpression(); if (docstringElement != null) { isFromClass = true; } else { inherited = ancestor.findMethodByName(methodName, false); } if (inherited != null) { docstringElement = inherited.getDocStringExpression(); } if (docstringElement != null) { final String inheritedDoc = docstringElement.getStringValue(); if (inheritedDoc.length() > 1) { myEpilog.addItem(BR).addItem(BR); String ancestor_name = ancestor.getName(); String marker = (pyClass == ancestor) ? PythonDocumentationProvider.LINK_TYPE_CLASS : PythonDocumentationProvider.LINK_TYPE_PARENT; final String ancestor_link = $().addWith(new LinkWrapper(marker + ancestor_name), $(ancestor_name)).toString(); if (isFromClass) { myEpilog.addItem(PyBundle.message("QDOC.copied.from.class.$0", ancestor_link)); } else { myEpilog.addItem(PyBundle.message("QDOC.copied.from.$0.$1", ancestor_link, methodName)); } myEpilog.addItem(BR).addItem(BR); ChainIterable<String> formatted = new ChainIterable<String>(); ChainIterable<String> unformatted = new ChainIterable<String>(); addFormattedDocString(pyFunction, inheritedDoc, formatted, unformatted); myEpilog.addWith(TagCode, formatted).add(unformatted); notFound = false; break; } } } if (notFound) { // above could have not worked because inheritance is not searched down to 'object'. // for well-known methods, copy built-in doc string. // TODO: also handle predefined __xxx__ that are not part of 'object'. if (PyNames.UnderscoredAttributes.contains(methodName)) { addPredefinedMethodDoc(pyFunction, methodName); } } }
@Override public boolean hasConflict(@NotNull final PyFunction member, @NotNull final PyClass aClass) { return NamePredicate.hasElementWithSameName(member, Arrays.asList(aClass.getMethods())); }
@NotNull @Override protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) { return Lists.<PyElement>newArrayList(filterNameless(Arrays.asList(pyClass.getMethods()))); }