mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
PY-76076 Acknowledge sys.version_info guards in flow-sensitive same-scope resolve
sys.version_info guards are processed at the level of ScopeImpl.collectDeclarations and PyResolveUtil.scopeCrawlUp in PyReferenceImpl.resolveInner, as implemented in 3318ff79cdcc5ba0ce5e4feb65abad5ad0f4acfa. However, once we collected all name definition candidates flow-insensitively this way, in PyReferenceImpl.getResultsFromProcessor, if the reference and these candidates were located in the same scope, we completely ignored these variants and gathered reachable definitions all over again from CFG using PyDefUseUtil.getLatestDefs. And the latter didn't consider version guards at all. I've added version guard checks directly in PyDefUseUtil.getLatestDefs. GitOrigin-RevId: 9f92eecd1eb1812bfbd2bf54f8192f45f0cf0a1d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
de0f128ac5
commit
ffceeb161b
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.controlflow.ControlFlowUtil;
|
||||
import com.intellij.codeInsight.controlflow.Instruction;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.Version;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.jetbrains.python.codeInsight.controlflow.CallInstruction;
|
||||
@@ -37,6 +38,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.python.psi.impl.stubs.PyVersionSpecificStubBaseKt.evaluateVersionsForElement;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
@@ -82,6 +85,9 @@ public final class PyDefUseUtil {
|
||||
final HashMap<PyCallSiteExpression, ConditionalInstruction> pendingTypeGuard = new HashMap<>();
|
||||
ControlFlowUtil.iteratePrev(startNum, instructions,
|
||||
instruction -> {
|
||||
if (unreachableDueToVersionGuard(instruction)) {
|
||||
return ControlFlowUtil.Operation.CONTINUE;
|
||||
}
|
||||
if (acceptTypeAssertions && instruction instanceof CallInstruction callInstruction) {
|
||||
var typeGuardInstruction = pendingTypeGuard.get(instruction.getElement());
|
||||
if (typeGuardInstruction != null) {
|
||||
@@ -97,7 +103,6 @@ public final class PyDefUseUtil {
|
||||
}
|
||||
}
|
||||
final PsiElement element = instruction.getElement();
|
||||
final PyImplicitImportNameDefiner implicit = PyUtil.as(element, PyImplicitImportNameDefiner.class);
|
||||
if (acceptTypeAssertions
|
||||
&& instruction instanceof ConditionalInstruction conditionalInstruction
|
||||
&& instruction.num() < startNum) {
|
||||
@@ -122,7 +127,7 @@ public final class PyDefUseUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (acceptImplicitImports && implicit != null) {
|
||||
else if (acceptImplicitImports && element instanceof PyImplicitImportNameDefiner implicit) {
|
||||
if (!implicit.multiResolveName(varName).isEmpty()) {
|
||||
result.add(instruction);
|
||||
return ControlFlowUtil.Operation.CONTINUE;
|
||||
@@ -133,6 +138,14 @@ public final class PyDefUseUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean unreachableDueToVersionGuard(@NotNull Instruction instruction) {
|
||||
PsiElement element = instruction.getElement();
|
||||
if (element == null) return false;
|
||||
LanguageLevel languageLevel = LanguageLevel.forElement(element);
|
||||
Version version = new Version(languageLevel.getMajorVersion(), languageLevel.getMinorVersion(), 0);
|
||||
return !evaluateVersionsForElement(element).contains(version);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String elementName(PsiElement element) {
|
||||
if (element instanceof PyImportElement) {
|
||||
|
||||
12
python/testData/types/GenericAliasUnderVersionGuard/mod.py
Normal file
12
python/testData/types/GenericAliasUnderVersionGuard/mod.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import sys
|
||||
from typing import TypeAlias, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
Alias: TypeAlias = list[T]
|
||||
else:
|
||||
Alias: TypeAlias = set[T]
|
||||
|
||||
def f(x: T) -> Alias[T]:
|
||||
pass
|
||||
@@ -0,0 +1,11 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
from builtins import list as Container
|
||||
else:
|
||||
from builtins import set as Container
|
||||
|
||||
|
||||
class C:
|
||||
def m(self) -> Container[str]:
|
||||
...
|
||||
@@ -5712,6 +5712,73 @@ public class PyTypingTest extends PyTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76076 PY-60968
|
||||
public void testGenericAliasUnderVersionGuard() {
|
||||
doMultiFileStubAwareTest("list[str]", """
|
||||
from mod import f
|
||||
|
||||
expr = f("foo")
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76076 PY-60968
|
||||
public void testGenericTypeImportedUnderVersionGuard() {
|
||||
doMultiFileStubAwareTest("list[str]", """
|
||||
from mod import C
|
||||
expr = C().m()
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76076
|
||||
public void testFunctionDefinitionUnderVersionGuard() {
|
||||
doTest("list[str]", """
|
||||
import sys
|
||||
from typing import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
def f(x: T) -> list[T]: ...
|
||||
else:
|
||||
def f(x: T) -> set[T]: ...
|
||||
|
||||
expr = f("foo")
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76076
|
||||
public void testClassDefinitionUnderVersionGuard() {
|
||||
doTest("list[str]", """
|
||||
import sys
|
||||
from typing import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
class C:
|
||||
def m(self, x: T) -> list[T]: ...
|
||||
else:
|
||||
class C:
|
||||
def m(self, x: T) -> set[T]: ...
|
||||
|
||||
expr = C().m("foo")
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76076
|
||||
public void testVariableDefinitionUnderVersionGuard() {
|
||||
doTest("int", """
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
x: str = "foo"
|
||||
else:
|
||||
x: int = 42
|
||||
|
||||
expr = x
|
||||
""");
|
||||
}
|
||||
|
||||
private void doTestNoInjectedText(@NotNull String text) {
|
||||
myFixture.configureByText(PythonFileType.INSTANCE, text);
|
||||
final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject());
|
||||
|
||||
Reference in New Issue
Block a user