From c13fbf623a3d8b6cbeeba8ac4ec92820917d541c Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Fri, 12 May 2017 19:14:04 +0300 Subject: [PATCH] PY-22971 Fixed: Support @typing.overload in regular Python files, not only in Python stubs Update PyRedeclarationInspection to not consider preceding overloads as write elements for the following implementation. --- .../PyRedeclarationInspection.java | 16 +++++++--- ...ementationOverloadImplementationInClass.py | 17 ++++++++++ .../overloadsAndImplementationInClass.py | 18 +++++++++++ .../overloadsAndImplementationsInClass.py | 21 +++++++++++++ ...oadImplementationOverloadImplementation.py | 19 ++++++++++++ .../topLevelOverloadsAndImplementation.py | 20 ++++++++++++ .../topLevelOverloadsAndImplementations.py | 24 ++++++++++++++ .../PyRedeclarationInspectionTest.java | 31 +++++++++++++++++++ 8 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 python/testData/inspections/PyRedeclarationInspection/overloadImplementationOverloadImplementationInClass.py create mode 100644 python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationInClass.py create mode 100644 python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationsInClass.py create mode 100644 python/testData/inspections/PyRedeclarationInspection/topLevelOverloadImplementationOverloadImplementation.py create mode 100644 python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementation.py create mode 100644 python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementations.py diff --git a/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java b/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java index 22a653dd8f89..c4650af37439 100644 --- a/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * 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. @@ -26,7 +26,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiNameIdentifierOwner; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.Function; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; @@ -35,6 +34,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix; import com.jetbrains.python.psi.*; +import com.jetbrains.python.pyi.PyiTypeProvider; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,6 +49,7 @@ import java.util.List; * @author vlan */ public class PyRedeclarationInspection extends PyInspection { + @Override @Nls @NotNull public String getDisplayName() { @@ -136,8 +137,11 @@ public class PyRedeclarationInspection extends PyInspection { if (rwInstruction.getAccess().isReadAccess()) { readElementRef.set(originalElement); } - if (rwInstruction.getAccess().isWriteAccess()) { - if (originalElement != element) { + if (rwInstruction.getAccess().isWriteAccess() && originalElement != element) { + if (element instanceof PyCallable && isOverload(originalElement)) { + return ControlFlowUtil.Operation.NEXT; + } + else { writeElementRef.set(originalElement); } } @@ -163,6 +167,10 @@ public class PyRedeclarationInspection extends PyInspection { } } + private boolean isOverload(@NotNull PsiElement element) { + return element instanceof PyCallable && PyiTypeProvider.isOverload((PyCallable)element, myTypeEvalContext); + } + private static boolean suggestRename(@NotNull PsiNameIdentifierOwner element, @NotNull PsiElement originalElement) { // Target expressions in the same scope are treated as the same variable if ((element instanceof PyTargetExpression) && originalElement instanceof PyTargetExpression) { diff --git a/python/testData/inspections/PyRedeclarationInspection/overloadImplementationOverloadImplementationInClass.py b/python/testData/inspections/PyRedeclarationInspection/overloadImplementationOverloadImplementationInClass.py new file mode 100644 index 000000000000..a930f9c7efbd --- /dev/null +++ b/python/testData/inspections/PyRedeclarationInspection/overloadImplementationOverloadImplementationInClass.py @@ -0,0 +1,17 @@ +from typing import overload + + +class A: + @overload + def foo(self, value: int) -> str: + pass + + def foo(self, value): + return None + + @overload + def foo(self, value: str) -> str: + pass + + def foo(self, value): + return None \ No newline at end of file diff --git a/python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationInClass.py b/python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationInClass.py new file mode 100644 index 000000000000..4e3e398016c1 --- /dev/null +++ b/python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationInClass.py @@ -0,0 +1,18 @@ +from typing import overload + + +class A: + @overload + def utf8(self, value: None) -> None: + pass + + @overload + def utf8(self, value: bytes) -> bytes: + pass + + @overload + def utf8(self, value: str) -> bytes: + pass + + def utf8(self, value): + return None \ No newline at end of file diff --git a/python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationsInClass.py b/python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationsInClass.py new file mode 100644 index 000000000000..8210d6e57385 --- /dev/null +++ b/python/testData/inspections/PyRedeclarationInspection/overloadsAndImplementationsInClass.py @@ -0,0 +1,21 @@ +from typing import overload + + +class A: + @overload + def utf8(self, value: None) -> None: + pass + + @overload + def utf8(self, value: bytes) -> bytes: + pass + + @overload + def utf8(self, value: str) -> bytes: + pass + + def utf8(self, value): + return None + + def utf8(self, value): + return None \ No newline at end of file diff --git a/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadImplementationOverloadImplementation.py b/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadImplementationOverloadImplementation.py new file mode 100644 index 000000000000..004096927a4b --- /dev/null +++ b/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadImplementationOverloadImplementation.py @@ -0,0 +1,19 @@ +from typing import overload + + +@overload +def foo(value: int) -> str: + pass + + +def foo(value): + return None + + +@overload +def foo(value: str) -> str: + pass + + +def foo(value): + return None \ No newline at end of file diff --git a/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementation.py b/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementation.py new file mode 100644 index 000000000000..10828a5ac41d --- /dev/null +++ b/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementation.py @@ -0,0 +1,20 @@ +from typing import overload + + +@overload +def utf8(value: None) -> None: + pass + + +@overload +def utf8(value: bytes) -> bytes: + pass + + +@overload +def utf8(value: str) -> bytes: + pass + + +def utf8(value): + return None \ No newline at end of file diff --git a/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementations.py b/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementations.py new file mode 100644 index 000000000000..24afb0ce8ec8 --- /dev/null +++ b/python/testData/inspections/PyRedeclarationInspection/topLevelOverloadsAndImplementations.py @@ -0,0 +1,24 @@ +from typing import overload + + +@overload +def utf8(value: None) -> None: + pass + + +@overload +def utf8(value: bytes) -> bytes: + pass + + +@overload +def utf8(value: str) -> bytes: + pass + + +def utf8(value): + return None + + +def utf8(value): + return None \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyRedeclarationInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyRedeclarationInspectionTest.java index e8fee694c710..918151e9e6f4 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyRedeclarationInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyRedeclarationInspectionTest.java @@ -16,6 +16,7 @@ package com.jetbrains.python.inspections; import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.LanguageLevel; public class PyRedeclarationInspectionTest extends PyTestCase { @@ -63,6 +64,36 @@ public class PyRedeclarationInspectionTest extends PyTestCase { doTest(); } + // PY-22971 + public void testTopLevelOverloadsAndImplementation() { + runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); + } + + // PY-22971 + public void testOverloadsAndImplementationInClass() { + runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); + } + + // PY-22971 + public void testTopLevelOverloadsAndImplementations() { + runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); + } + + // PY-22971 + public void testOverloadsAndImplementationsInClass() { + runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); + } + + // PY-22971 + public void testTopLevelOverloadImplementationOverloadImplementation() { + runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); + } + + // PY-22971 + public void testOverloadImplementationOverloadImplementationInClass() { + runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); + } + private void doTest() { final String path = "inspections/PyRedeclarationInspection/" + getTestName(true) + ".py";