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.
This commit is contained in:
Semyon Proshev
2017-05-13 00:17:46 +03:00
committed by Semyon Proshev
parent f0ab4154ef
commit c13fbf623a
8 changed files with 162 additions and 4 deletions
@@ -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) {
@@ -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 <warning descr="Redeclared 'foo' defined above without usage">foo</warning>(self, value):
return None
@@ -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
@@ -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 <warning descr="Redeclared 'utf8' defined above without usage">utf8</warning>(self, value):
return None
@@ -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 <warning descr="Redeclared 'foo' defined above without usage">foo</warning>(value):
return None
@@ -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
@@ -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 <warning descr="Redeclared 'utf8' defined above without usage">utf8</warning>(value):
return None
@@ -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";