diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java index ebc82f3299b1..224190372396 100644 --- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java @@ -118,7 +118,7 @@ public class PyArgumentListInspection extends PyInspection { final PyFunction function = (PyFunction)callable; // Decorate functions may have different parameter lists. We don't match arguments with parameters of decorators yet - if (PyKnownDecoratorUtil.hasNonBuiltinDecorator(function, context) || + if (PyKnownDecoratorUtil.hasUnknownOrChangingSignatureDecorator(function, context) || decoratedClassInitCall(call.getCallee(), function, context)) { return; } @@ -144,7 +144,9 @@ public class PyArgumentListInspection extends PyInspection { return Arrays .stream(classReference.multiResolve(false)) .map(ResolveResult::getElement) - .anyMatch(element -> element instanceof PyClass && PyKnownDecoratorUtil.hasNonBuiltinDecorator((PyClass)element, context)); + .anyMatch( + element -> element instanceof PyClass && PyKnownDecoratorUtil.hasUnknownOrChangingReturnTypeDecorator((PyClass)element, context) + ); } return false; diff --git a/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java b/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java index e0c967d4d10b..a62721dbde91 100644 --- a/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java @@ -68,7 +68,9 @@ public class PyNoneFunctionAssignmentInspection extends PyInspection { if (callable instanceof PyFunction) { final PyFunction function = (PyFunction)callable; // Currently we don't infer types returned by decorators - if (hasInheritors(function) || PyKnownDecoratorUtil.hasNonBuiltinDecorator(function, myTypeEvalContext)) { + if (hasInheritors(function) || + PyKnownDecoratorUtil.hasUnknownOrChangingReturnTypeDecorator(function, myTypeEvalContext) || + PyKnownDecoratorUtil.hasAbstractDecorator(function, myTypeEvalContext)) { return true; } } diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index c68923d975f7..4e8f846dc503 100644 --- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -86,6 +86,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return (PyUnresolvedReferencesInspection)inspectionProfile.getUnwrappedTool(SHORT_NAME_KEY.toString(), element); } + @Override @Nls @NotNull public String getDisplayName() { @@ -805,7 +806,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } if (type instanceof PyFunctionTypeImpl) { final PyCallable callable = ((PyFunctionTypeImpl)type).getCallable(); - if (callable instanceof PyFunction && PyKnownDecoratorUtil.hasNonBuiltinDecorator((PyFunction)callable, myTypeEvalContext)) { + if (callable instanceof PyFunction && PyKnownDecoratorUtil.hasUnknownDecorator((PyFunction)callable, myTypeEvalContext)) { return true; } } diff --git a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java index 169355f939c6..4fd836d398d7 100644 --- a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java +++ b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.python.psi; import com.intellij.psi.PsiElement; @@ -100,7 +86,6 @@ public class PyKnownDecoratorUtil { } } - private static final Set BUILTIN_DECORATORS = EnumSet.of(PROPERTY, CLASSMETHOD, STATICMETHOD, TYPING_OVERLOAD); private static final Set ABSTRACT_DECORATORS = EnumSet.of(ABC_ABSTRACTMETHOD, ABC_ABSTRACTPROPERTY, ABC_ABSTRACTSTATICMETHOD, @@ -192,23 +177,6 @@ public class PyKnownDecoratorUtil { return !allDecoratorsAreKnown(element, getKnownDecorators(element, context)); } - /** - * Checks that given element has any non-builtin decorators. - * - * @param element decoratable element to check - * @param context type evaluation context. If it doesn't allow switch to AST, decorators will be compared by the text of the last component - * of theirs qualified names. - * @see PyKnownDecoratorUtil.KnownDecorator - */ - public static boolean hasNonBuiltinDecorator(@NotNull PyDecoratable element, @NotNull TypeEvalContext context) { - final List knownDecorators = getKnownDecorators(element, context); - if (!allDecoratorsAreKnown(element, knownDecorators)) { - return true; - } - knownDecorators.removeAll(BUILTIN_DECORATORS); - return !knownDecorators.isEmpty(); - } - /** * Checks that given function has any decorators from {@code abc} module. * @@ -238,6 +206,16 @@ public class PyKnownDecoratorUtil { return getKnownDecorators(function, context).contains(TYPING_OVERLOAD); } + public static boolean hasUnknownOrChangingSignatureDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) { + final List decorators = getKnownDecorators(decoratable, context); + return !allDecoratorsAreKnown(decoratable, decorators) || decorators.contains(UNITTEST_MOCK_PATCH); + } + + public static boolean hasUnknownOrChangingReturnTypeDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) { + final List decorators = getKnownDecorators(decoratable, context); + return !allDecoratorsAreKnown(decoratable, decorators) || decorators.contains(UNITTEST_MOCK_PATCH); + } + private static boolean allDecoratorsAreKnown(@NotNull PyDecoratable element, @NotNull List decorators) { final PyDecoratorList decoratorList = element.getDecoratorList(); return decoratorList == null ? decorators.isEmpty() : decoratorList.getDecorators().length == decorators.size(); diff --git a/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java b/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java index fcc33d326360..0deac424472c 100644 --- a/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java +++ b/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java @@ -69,7 +69,7 @@ public class CompletionVariantsProcessor extends VariantsProcessor { if (!mySuppressParentheses && element instanceof PyFunction && ((PyFunction)element).getProperty() == null && - !PyKnownDecoratorUtil.hasNonBuiltinDecorator((PyFunction)element, context) && + !PyKnownDecoratorUtil.hasUnknownDecorator((PyFunction)element, context) && !isSingleArgDecoratorCall(myContext, (PyFunction)element)) { item = item.withInsertHandler(PyFunctionInsertHandler.INSTANCE); final List parameters = ((PyFunction)element).getParameters(context); diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index c2c62741f96b..65d50b18a07b 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -371,7 +371,8 @@ public class PyTypeChecker { } if (type instanceof PyFunctionType) { final PyCallable callable = ((PyFunctionType)type).getCallable(); - if (callable instanceof PyDecoratable && PyKnownDecoratorUtil.hasUnknownDecorator((PyDecoratable)callable, context)){ + if (callable instanceof PyDecoratable && + PyKnownDecoratorUtil.hasUnknownOrChangingReturnTypeDecorator((PyDecoratable)callable, context)){ return true; } } diff --git a/python/testData/inspections/PyArgumentListInspection/abstractMethod.py b/python/testData/inspections/PyArgumentListInspection/abstractMethod.py new file mode 100644 index 000000000000..a6f1ed3b6781 --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/abstractMethod.py @@ -0,0 +1,10 @@ +from abc import ABCMeta, abstractmethod + + +class IUserService(metaclass=ABCMeta): + @abstractmethod + def get_user_by_id(self, *, user_id): + pass + +the_user_service = get_service(IUserService) # type: IUserService +user = the_user_service.get_user_by_id(123) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index 340d65dfb000..1951579b8db6 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.python.inspections; import com.jetbrains.python.fixtures.PyInspectionTestCase; @@ -327,4 +313,9 @@ public class PyArgumentListInspectionTest extends PyInspectionTestCase { public void testKwargsAgainstKeywordOnly() { runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); } + + // PY-26023 + public void testAbstractMethod() { + runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTest); + } }