mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-88281 typing: partially unresolved union leads to complete Any
(cherry picked from commit 12afe1b92d81a439887680383ae0e4afbb8decb7) GitOrigin-RevId: 6796d0fd9da021e9f66165021be3b29c508515d3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
5d241e05ff
commit
069606d508
+23
-24
@@ -1190,7 +1190,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
|
||||
}
|
||||
|
||||
private fun typeHasOverloadedBitwiseOr(
|
||||
type: PyType, expression: PyExpression,
|
||||
type: PyType, expression: PyExpression, operator: String = "__or__",
|
||||
context: Context,
|
||||
): Boolean {
|
||||
if (type !is PyClassType) {
|
||||
@@ -1202,7 +1202,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
|
||||
return false
|
||||
}
|
||||
val resolved = metaClassType
|
||||
.resolveMember("__or__", expression, AccessDirection.READ, PyResolveContext.defaultContext(typeContext))
|
||||
.resolveMember(operator, expression, AccessDirection.READ, PyResolveContext.defaultContext(typeContext))
|
||||
if (resolved.isNullOrEmpty()) return false
|
||||
|
||||
return StreamEx.of(resolved)
|
||||
@@ -1279,11 +1279,11 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
|
||||
if (neverType != null) {
|
||||
return Ref(neverType)
|
||||
}
|
||||
val unionType: Ref<PyType?>? = getUnionType(resolved, context)
|
||||
val unionType = getUnionType(resolved, context)
|
||||
if (unionType != null) {
|
||||
return unionType
|
||||
}
|
||||
val intersectionType: Ref<PyType?>? = getIntersectionType(resolved, context)
|
||||
val intersectionType = getIntersectionType(resolved, context)
|
||||
if (intersectionType != null) {
|
||||
return intersectionType
|
||||
}
|
||||
@@ -1467,19 +1467,16 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
|
||||
}
|
||||
|
||||
private fun getIntersectionType(resolved: PsiElement, context: Context): Ref<PyType?>? {
|
||||
if (resolved is PyBinaryExpression && resolved.operator === PyTokenTypes.AND) {
|
||||
val left = resolved.leftExpression
|
||||
val right = resolved.rightExpression
|
||||
if (left == null || right == null) return null
|
||||
if (resolved !is PyBinaryExpression || resolved.operator !== PyTokenTypes.AND) return null
|
||||
val left = resolved.leftExpression ?: return null
|
||||
val right = resolved.rightExpression ?: return null
|
||||
|
||||
val leftTypeRef: Ref<PyType?>? = getType(left, context)
|
||||
val rightTypeRef: Ref<PyType?>? = getType(right, context)
|
||||
if (leftTypeRef == null || rightTypeRef == null) return null
|
||||
val leftTypeRef = getType(left, context)
|
||||
val rightTypeRef = getType(right, context)
|
||||
if (leftTypeRef == null && rightTypeRef == null) return null
|
||||
|
||||
val intersection = intersection(leftTypeRef.get(), rightTypeRef.get())
|
||||
return if (intersection != null) Ref(intersection) else null
|
||||
}
|
||||
return null
|
||||
val intersection = intersection(leftTypeRef?.get(), rightTypeRef?.get())
|
||||
return intersection?.let { Ref(it) }
|
||||
}
|
||||
|
||||
private fun getNoneType(typeHint: PyExpression, resolved: PsiElement): Ref<PyType?>? {
|
||||
@@ -2095,18 +2092,20 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
|
||||
}
|
||||
}
|
||||
else if (element is PyBinaryExpression && element.operator === PyTokenTypes.OR) {
|
||||
val left = element.leftExpression
|
||||
val right = element.rightExpression
|
||||
if (left == null || right == null) return null
|
||||
val left = element.leftExpression ?: return null
|
||||
val right = element.rightExpression ?: return null
|
||||
|
||||
val leftTypeRef: Ref<PyType?>? = getType(left, context)
|
||||
val rightTypeRef: Ref<PyType?>? = getType(right, context)
|
||||
if (leftTypeRef == null || rightTypeRef == null) return null
|
||||
val leftTypeRef = getType(left, context)
|
||||
val rightTypeRef = getType(right, context)
|
||||
if (leftTypeRef == null && rightTypeRef == null) return null
|
||||
|
||||
val leftType = leftTypeRef.get()
|
||||
if (leftType != null && typeHasOverloadedBitwiseOr(leftType, left, context)) return null
|
||||
// if the class type defines __or__ then don't create a union
|
||||
val leftType = leftTypeRef?.get()
|
||||
if (leftType != null && typeHasOverloadedBitwiseOr(leftType, left, context = context)) return null
|
||||
val rightType = rightTypeRef?.get()
|
||||
if (rightType != null && typeHasOverloadedBitwiseOr(rightType, left, "__ror__", context)) return null
|
||||
|
||||
val union = PyUnionType.union(leftType, rightTypeRef.get())
|
||||
val union = PyUnionType.union(leftType, rightType)
|
||||
return if (union != null) Ref(union) else null
|
||||
}
|
||||
return null
|
||||
|
||||
@@ -920,6 +920,7 @@ public abstract class PyCompatibilityVisitor extends PyElementVisitor {
|
||||
node, new ReplaceWithOldStyleUnionQuickFix(), new AddFromFutureImportAnnotationsQuickFix());
|
||||
}
|
||||
else {
|
||||
if (node.getLeftExpression().getType(context) == null) return;
|
||||
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON310),
|
||||
PyPsiBundle.message("INSP.compatibility.new.union.syntax.not.available.in.earlier.version"),
|
||||
node, new ReplaceWithOldStyleUnionQuickFix());
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
@@ -119,6 +105,30 @@ public class Py3HighlightingTest extends PyTestCase {
|
||||
doTestWithLanguageLevel(LanguageLevel.PYTHON39, false, false);
|
||||
}
|
||||
|
||||
public void testNoErrorMetaClassOverloadBitwiseRorChain() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON39,
|
||||
() -> {
|
||||
myFixture.configureByText(
|
||||
"test.py", """
|
||||
class M(type):
|
||||
def __ror__(self, other):
|
||||
return other
|
||||
|
||||
class A(metaclass=M):
|
||||
...
|
||||
class B(metaclass=M):
|
||||
...
|
||||
class C(metaclass=M):
|
||||
...
|
||||
|
||||
print(A | B | C)
|
||||
""");
|
||||
myFixture.testHighlighting(false, false, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// PY-32067
|
||||
public void testAwaitInNonAsyncFunction() {
|
||||
doHighlightingQuickfixTest("Convert to async function");
|
||||
|
||||
@@ -5229,6 +5229,32 @@ public class Py3TypeTest extends PyTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
@TestFor(issues = "PY-88281")
|
||||
public void testUnionPartialUnresolved() {
|
||||
doTest("int | Any", """
|
||||
expr: int | asdf
|
||||
""");
|
||||
}
|
||||
|
||||
@TestFor(issues = "PY-88281")
|
||||
public void testIntersectionPartialUnresolved() {
|
||||
doTest("int & Any", """
|
||||
expr: int & asdf
|
||||
""");
|
||||
}
|
||||
|
||||
public void testRightHandOrClass() {
|
||||
doTest("UnionType | type[str] | int", """
|
||||
class M(type):
|
||||
def __ror__(self, other: object) -> int:
|
||||
return 1
|
||||
|
||||
class A(metaclass=M): ...
|
||||
|
||||
expr = str | A
|
||||
""");
|
||||
}
|
||||
|
||||
private void doTest(final String expectedType, final String text) {
|
||||
myFixture.configureByText(PythonFileType.INSTANCE, text);
|
||||
final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.jetbrains.python.fixtures.PyInspectionTestCase;
|
||||
@@ -1549,7 +1549,7 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase {
|
||||
doTestByText("""
|
||||
from typing import Self
|
||||
|
||||
something: <warning descr="Invalid type annotation"><warning descr="Cannot use 'Self' outside class">Self</warning> | None</warning> = None
|
||||
something: <warning descr="Cannot use 'Self' outside class">Self</warning> | None = None
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user