PY-81936 Code unreachable under sys.version_info guards is not highlighted

(cherry picked from commit ea1c276b5648f0ca4237b770179b27d6c5ee1048)

IJ-MR-167790

GitOrigin-RevId: e76f0a1150903d9eb85141c6c4ac898f67584d3d
This commit is contained in:
Aleksandr.Govenko
2025-06-13 16:19:34 +02:00
committed by intellij-monorepo-bot
parent fefa1ad70a
commit 9575aedee8
3 changed files with 35 additions and 8 deletions

View File

@@ -68,7 +68,7 @@ public final class ControlFlowCache {
public static @NotNull PyDataFlow getDataFlow(@NotNull ScopeOwner element, @NotNull TypeEvalContext context) {
// Cache will reset on psi modification, same as TypeEvalContext
return PyUtil.getParameterizedCachedValue(element, context, (ctx) -> {
return new PyDataFlow(getControlFlow(element), context);
return new PyDataFlow(element, getControlFlow(element), context);
});
}
}

View File

@@ -2,19 +2,27 @@ package com.jetbrains.python.codeInsight.controlflow
import com.intellij.codeInsight.controlflow.ControlFlow
import com.intellij.codeInsight.controlflow.Instruction
import com.intellij.openapi.util.Version
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
import com.jetbrains.python.psi.*
import com.jetbrains.python.psi.impl.PyEvaluator
import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher
import com.jetbrains.python.psi.impl.stubs.evaluateVersionsForElement
import com.jetbrains.python.psi.types.PyNeverType
import com.jetbrains.python.psi.types.TypeEvalContext
import org.jetbrains.annotations.ApiStatus
import java.util.*
@ApiStatus.Internal
class PyDataFlow(controlFlow: ControlFlow, private val context: TypeEvalContext) : ControlFlow by controlFlow {
class PyDataFlow(scopeOwner: ScopeOwner, controlFlow: ControlFlow, private val context: TypeEvalContext) : ControlFlow by controlFlow {
private val reachability: BooleanArray = BooleanArray(instructions.size)
private val languageVersion = run {
val languageLevel = PythonLanguageLevelPusher.getLanguageLevelForFile(scopeOwner.containingFile)
Version(languageLevel.majorVersion, languageLevel.minorVersion, 0)
}
init {
buildReachability()
@@ -45,19 +53,25 @@ class PyDataFlow(controlFlow: ControlFlow, private val context: TypeEvalContext)
private fun getReachableSuccessors(instruction: Instruction): Collection<Instruction> {
if (instruction is CallInstruction && instruction.isNoReturnCall(context)) return emptyList()
if (instruction is PyWithContextExitInstruction && !instruction.isSuppressingExceptions(context)) return emptyList()
return instruction.allSucc().filter { next: Instruction ->
if (next is ReadWriteInstruction && next.access.isAssertTypeAccess) {
val type = next.getType(context, null)
return@filter !(type != null && type.get() is PyNeverType)
return instruction.allSucc()
.filter { it.isReachableWithVersionChecks() }
.filter { next: Instruction ->
if (next is ReadWriteInstruction && next.access.isAssertTypeAccess) {
val type = next.getType(context, null)
return@filter !(type != null && type.get() is PyNeverType)
}
return@filter true
}
return@filter true
}
}
fun isUnreachable(instruction: Instruction): Boolean {
if (instruction.num() >= reachability.size) return false
return !reachability[instruction.num()]
}
private fun Instruction.isReachableWithVersionChecks(): Boolean {
return evaluateVersionsForElement(element ?: return true).contains(languageVersion)
}
}
/**

View File

@@ -24,6 +24,19 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase {
public void testUnreachable() {
runWithLanguageLevel(LanguageLevel.PYTHON26, () -> doTest());
}
// PY-81936
public void testUnreachableWithLangLevel() {
runWithLanguageLevel(LanguageLevel.PYTHON310, () -> doTestByText("""
import sys
if sys.version_info < (2, 7):
<warning descr="This code is unreachable">print("Unreachable")</warning>
if sys.version_info > (3, 11):
<warning descr="This code is unreachable">print("Unreachable")</warning>
"""));
}
// PY-81947
public void testAnyOrNoneAfterIsNotNoneCast(){