diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/impl/PyVersionCheck.kt b/python/python-psi-api/src/com/jetbrains/python/psi/impl/PyVersionCheck.kt deleted file mode 100644 index dbe544964f4c..000000000000 --- a/python/python-psi-api/src/com/jetbrains/python/psi/impl/PyVersionCheck.kt +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.psi.impl - -import com.google.common.collect.ImmutableRangeSet -import com.google.common.collect.Range -import com.intellij.openapi.util.Version -import com.intellij.psi.util.QualifiedName -import com.jetbrains.python.PyTokenTypes -import com.jetbrains.python.psi.* -import org.jetbrains.annotations.ApiStatus -import java.math.BigInteger - -@ApiStatus.Internal -object PyVersionCheck { - /** - * @return Version ranges if {@code expression} is a version check, {@code null} otherwise - * - * @see Version and Platform Checks - */ - @JvmStatic - fun convertToVersionRanges(ifPart: PyIfPart): ImmutableRangeSet? { - return ifPart.condition?.let(PyVersionCheck::convertToVersionRanges) - } - - fun convertToVersionRanges(expression: PyExpression): ImmutableRangeSet? { - val binaryExpr = PyPsiUtils.flattenParens(expression) as? PyBinaryExpression ?: return null - when (val operator = binaryExpr.operator) { - PyTokenTypes.AND_KEYWORD, PyTokenTypes.OR_KEYWORD -> { - val rhs = binaryExpr.rightExpression ?: return null - val ranges1 = convertToVersionRanges(binaryExpr.leftExpression) ?: return null - val ranges2 = convertToVersionRanges(rhs) ?: return null - return if (operator === PyTokenTypes.AND_KEYWORD) - ranges1.intersection(ranges2) - else - ranges1.union(ranges2) - } - - PyTokenTypes.LT, PyTokenTypes.GT, PyTokenTypes.LE, PyTokenTypes.GE -> { - val refExpr = PyPsiUtils.flattenParens(binaryExpr.leftExpression) as? PyReferenceExpression ?: return null - if (SYS_VERSION_INFO_QUALIFIED_NAME != refExpr.asQualifiedName()) return null - - val tuple = PyPsiUtils.flattenParens(binaryExpr.rightExpression) as? PyTupleExpression ?: return null - val version = evaluateVersion(tuple) ?: return null - - val range = when (operator) { - PyTokenTypes.LT -> Range.lessThan(version) - PyTokenTypes.GT -> Range.greaterThan(version) - PyTokenTypes.LE -> Range.atMost(version) - PyTokenTypes.GE -> Range.atLeast(version) - else -> throw IllegalStateException() - } - return ImmutableRangeSet.of(range) - } - - else -> return null - } - } - - private val SYS_VERSION_INFO_QUALIFIED_NAME = QualifiedName.fromDottedString("sys.version_info") - - private fun evaluateVersion(versionTuple: PyTupleExpression): Version? { - val elements = versionTuple.elements - if (elements.size != 1 && elements.size != 2) { - return null - } - - val major = evaluateNumber(elements[0]) - if (major == null) { - return null - } - - if (elements.size == 1) { - return Version(major, 0, 0) - } - - val minor = evaluateNumber(elements[1]) - if (minor == null) { - return null - } - - return Version(major, minor, 0) - } - - private fun evaluateNumber(expression: PyExpression?): Int? { - if (expression !is PyNumericLiteralExpression) return null - if (!expression.isIntegerLiteral) return null - val value = expression.bigIntegerValue - val intValue = value.toInt() - return if (BigInteger.valueOf(intValue.toLong()) == value) intValue else null - } -} \ No newline at end of file diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyVersionSpecificStubBase.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyVersionSpecificStubBase.kt index 1e88039ff2c3..3da0c184a333 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyVersionSpecificStubBase.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyVersionSpecificStubBase.kt @@ -6,9 +6,12 @@ import com.intellij.psi.PsiElement import com.intellij.psi.stubs.* import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.QualifiedName +import com.jetbrains.python.PyTokenTypes import com.jetbrains.python.psi.* -import com.jetbrains.python.psi.impl.PyVersionCheck +import com.jetbrains.python.psi.impl.PyPsiUtils import com.jetbrains.python.psi.stubs.PyVersionSpecificStub +import java.math.BigInteger internal abstract class PyVersionSpecificStubBase( parent: StubElement<*>?, @@ -49,7 +52,7 @@ internal fun evaluateVersionsForElement(element: PsiElement): ImmutableRangeSet< private fun evaluateVersionRangeForIfStatementPart(ifStatement: PyIfStatement, ifStatementPart: PsiElement): RangeSet? { assert(ifStatementPart is PyIfPart || ifStatementPart is PyElsePart) val result = if (ifStatementPart is PyIfPart) { - val versionRanges = PyVersionCheck.convertToVersionRanges(ifStatementPart) ?: return null + val versionRanges = convertToVersionRanges(ifStatementPart) ?: return null TreeRangeSet.create(versionRanges) } else { @@ -57,12 +60,88 @@ private fun evaluateVersionRangeForIfStatementPart(ifStatement: PyIfStatement, i } val ifParts = sequenceOf(ifStatement.ifPart) + ifStatement.elifParts.asSequence() for (ifPart in ifParts.takeWhile { it !== ifStatementPart }) { - val versionRanges = PyVersionCheck.convertToVersionRanges(ifPart) ?: return null + val versionRanges = convertToVersionRanges(ifPart) ?: return null result.removeAll(versionRanges) } return result } +/** + * @return Version ranges if {@code expression} is a version check, {@code null} otherwise + * + * @see Version and Platform Checks + */ +private fun convertToVersionRanges(ifPart: PyIfPart): ImmutableRangeSet? { + return ifPart.condition?.let { convertToVersionRanges(it) } +} + +private fun convertToVersionRanges(expression: PyExpression): ImmutableRangeSet? { + val binaryExpr = PyPsiUtils.flattenParens(expression) as? PyBinaryExpression ?: return null + when (val operator = binaryExpr.operator) { + PyTokenTypes.AND_KEYWORD, PyTokenTypes.OR_KEYWORD -> { + val rhs = binaryExpr.rightExpression ?: return null + val ranges1 = convertToVersionRanges(binaryExpr.leftExpression) ?: return null + val ranges2 = convertToVersionRanges(rhs) ?: return null + return if (operator === PyTokenTypes.AND_KEYWORD) + ranges1.intersection(ranges2) + else + ranges1.union(ranges2) + } + + PyTokenTypes.LT, PyTokenTypes.GT, PyTokenTypes.LE, PyTokenTypes.GE -> { + val refExpr = PyPsiUtils.flattenParens(binaryExpr.leftExpression) as? PyReferenceExpression ?: return null + if (SYS_VERSION_INFO_QUALIFIED_NAME != refExpr.asQualifiedName()) return null + + val tuple = PyPsiUtils.flattenParens(binaryExpr.rightExpression) as? PyTupleExpression ?: return null + val version = evaluateVersion(tuple) ?: return null + + val range = when (operator) { + PyTokenTypes.LT -> Range.lessThan(version) + PyTokenTypes.GT -> Range.greaterThan(version) + PyTokenTypes.LE -> Range.atMost(version) + PyTokenTypes.GE -> Range.atLeast(version) + else -> throw IllegalStateException() + } + return ImmutableRangeSet.of(range) + } + + else -> return null + } +} + +private val SYS_VERSION_INFO_QUALIFIED_NAME = QualifiedName.fromDottedString("sys.version_info") + +private fun evaluateVersion(versionTuple: PyTupleExpression): Version? { + val elements = versionTuple.elements + if (elements.size != 1 && elements.size != 2) { + return null + } + + val major = evaluateNumber(elements[0]) + if (major == null) { + return null + } + + if (elements.size == 1) { + return Version(major, 0, 0) + } + + val minor = evaluateNumber(elements[1]) + if (minor == null) { + return null + } + + return Version(major, minor, 0) +} + +private fun evaluateNumber(expression: PyExpression?): Int? { + if (expression !is PyNumericLiteralExpression) return null + if (!expression.isIntegerLiteral) return null + val value = expression.bigIntegerValue + val intValue = value.toInt() + return if (BigInteger.valueOf(intValue.toLong()) == value) intValue else null +} + internal fun serializeVersions(versions: RangeSet, outputStream: StubOutputStream) { val ranges = versions.asRanges() outputStream.writeVarInt(ranges.size)