@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(); }
@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; }
@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; }
/** * 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 } }
/** * 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; }
protected String getElementLocation() { final PyClass containingClass = getContainingClass(); if (containingClass != null) { return "(" + containingClass.getName() + " in " + getPackageForFile(getContainingFile()) + ")"; } return super.getElementLocation(); }
@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 @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; }
// 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; }
@NotNull @Override protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) { return Lists.<PyElement>newArrayList(filterNameless(Arrays.asList(pyClass.getMethods()))); }
@Override public boolean hasConflict(@NotNull final PyFunction member, @NotNull final PyClass aClass) { return NamePredicate.hasElementWithSameName(member, Arrays.asList(aClass.getMethods())); }