mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Improve PyKnownDecoratorUtil to support more decorator side-effects (PY-26023)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<KnownDecorator> BUILTIN_DECORATORS = EnumSet.of(PROPERTY, CLASSMETHOD, STATICMETHOD, TYPING_OVERLOAD);
|
||||
private static final Set<KnownDecorator> 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<KnownDecorator> 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<KnownDecorator> 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<KnownDecorator> decorators = getKnownDecorators(decoratable, context);
|
||||
return !allDecoratorsAreKnown(decoratable, decorators) || decorators.contains(UNITTEST_MOCK_PATCH);
|
||||
}
|
||||
|
||||
private static boolean allDecoratorsAreKnown(@NotNull PyDecoratable element, @NotNull List<KnownDecorator> decorators) {
|
||||
final PyDecoratorList decoratorList = element.getDecoratorList();
|
||||
return decoratorList == null ? decorators.isEmpty() : decoratorList.getDecorators().length == decorators.size();
|
||||
|
||||
@@ -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<PyCallableParameter> parameters = ((PyFunction)element).getParameters(context);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(<warning descr="Unexpected argument">123</warning><warning descr="Parameter 'user_id' unfilled">)</warning>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user