diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/union.md b/crates/ty_python_semantic/resources/mdtest/annotations/union.md index 1fc2ba5f7072bf..262941421b68ac 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/union.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/union.md @@ -124,4 +124,10 @@ class Foo: d = {} d[0]: int | str = 42 + +# these are still errors: `from __future__ import annotations` +# only stringifies *type annotations*, not arbitrary runtime expressions + +X = str | int # error: [unsupported-operator] +Y = tuple[str | int, ...] # error: [unsupported-operator] ``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/annotations.md_-_Assignment_with_anno\342\200\246_-_PEP-604_in_non-type-\342\200\246_-_Earlier_versions_(f2859c9800f37c7).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/annotations.md_-_Assignment_with_anno\342\200\246_-_PEP-604_in_non-type-\342\200\246_-_Earlier_versions_(f2859c9800f37c7).snap" index 1ccae988b6b773..a8f30701603b67 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/annotations.md_-_Assignment_with_anno\342\200\246_-_PEP-604_in_non-type-\342\200\246_-_Earlier_versions_(f2859c9800f37c7).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/annotations.md_-_Assignment_with_anno\342\200\246_-_PEP-604_in_non-type-\342\200\246_-_Earlier_versions_(f2859c9800f37c7).snap" @@ -30,7 +30,7 @@ error[unsupported-operator]: Unsupported `|` operation | | Has type `` | Has type `` | -info: Note that `X | Y` PEP 604 union syntax is only available in Python 3.10 and later +info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted info: Python 3.9 was assumed when resolving types because it was specified on the command line info: rule `unsupported-operator` is enabled by default diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/union.md_-_Union_-_Diagnostics_for_PEP-\342\200\246_(8fa61a3cfe810040).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/union.md_-_Union_-_Diagnostics_for_PEP-\342\200\246_(8fa61a3cfe810040).snap" index f8754cd1ff5d3d..3c1a284615a5e3 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/union.md_-_Union_-_Diagnostics_for_PEP-\342\200\246_(8fa61a3cfe810040).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/union.md_-_Union_-_Diagnostics_for_PEP-\342\200\246_(8fa61a3cfe810040).snap" @@ -36,6 +36,12 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/annotations/union.md 8 | 9 | d = {} 10 | d[0]: int | str = 42 +11 | +12 | # these are still errors: `from __future__ import annotations` +13 | # only stringifies *type annotations*, not arbitrary runtime expressions +14 | +15 | X = str | int # error: [unsupported-operator] +16 | Y = tuple[str | int, ...] # error: [unsupported-operator] ``` # Diagnostics @@ -53,7 +59,7 @@ error[unsupported-operator]: Unsupported `|` operation 3 | class Foo: | info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted -info: Python 3.9 was assumed when inferring types because it was specified on the command line +info: Python 3.9 was assumed when resolving types because it was specified on the command line info: rule `unsupported-operator` is enabled by default ``` @@ -73,7 +79,7 @@ error[unsupported-operator]: Unsupported `|` operation 7 | d = {} | info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted -info: Python 3.9 was assumed when inferring types because it was specified on the command line +info: Python 3.9 was assumed when resolving types because it was specified on the command line info: rule `unsupported-operator` is enabled by default ``` @@ -90,7 +96,45 @@ error[unsupported-operator]: Unsupported `|` operation | Has type `` | info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted -info: Python 3.9 was assumed when inferring types because it was specified on the command line +info: Python 3.9 was assumed when resolving types because it was specified on the command line +info: rule `unsupported-operator` is enabled by default + +``` + +``` +error[unsupported-operator]: Unsupported `|` operation + --> src/b.py:15:5 + | +13 | # only stringifies *type annotations*, not arbitrary runtime expressions +14 | +15 | X = str | int # error: [unsupported-operator] + | ---^^^--- + | | | + | | Has type `` + | Has type `` +16 | Y = tuple[str | int, ...] # error: [unsupported-operator] + | +info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted +info: `from __future__ import annotations` has no effect outside type annotations +info: Python 3.9 was assumed when resolving types because it was specified on the command line +info: rule `unsupported-operator` is enabled by default + +``` + +``` +error[unsupported-operator]: Unsupported `|` operation + --> src/b.py:16:11 + | +15 | X = str | int # error: [unsupported-operator] +16 | Y = tuple[str | int, ...] # error: [unsupported-operator] + | ---^^^--- + | | | + | | Has type `` + | Has type `` + | +info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted +info: `from __future__ import annotations` has no effect outside type annotations +info: Python 3.9 was assumed when resolving types because it was specified on the command line info: rule `unsupported-operator` is enabled by default ``` diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index cc0dddc74cb758..692e24056ad278 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -11,7 +11,7 @@ use crate::lint::{Level, LintRegistryBuilder, LintStatus}; use crate::place::{DefinedPlace, Place}; use crate::semantic_index::definition::{Definition, DefinitionKind}; use crate::semantic_index::place::{PlaceTable, ScopedPlaceId}; -use crate::semantic_index::{global_scope, place_table, use_def_map}; +use crate::semantic_index::{SemanticIndex, global_scope, place_table, use_def_map}; use crate::suppression::FileSuppressionId; use crate::types::call::CallError; use crate::types::class::{CodeGeneratorKind, DisjointBase, DisjointBaseKind, MethodDecorator}; @@ -5699,6 +5699,7 @@ pub(super) fn report_unsupported_augmented_assignment<'db>( pub(super) fn report_unsupported_binary_operation<'db>( context: &InferContext<'db, '_>, + index: &SemanticIndex<'db>, binary_expression: &ast::ExprBinOp, left_ty: Type<'db>, right_ty: Type<'db>, @@ -5724,11 +5725,21 @@ pub(super) fn report_unsupported_binary_operation<'db>( || right_ty.is_subtype_of(db, KnownClass::Type.to_instance(db))) && Program::get(db).python_version(db) < PythonVersion::PY310 { - diagnostic.info( - "Note that `X | Y` PEP 604 union syntax is only available in Python 3.10 and later", - ); - add_inferred_python_version_hint_to_diagnostic(db, &mut diagnostic, "resolving types"); + note_py_version_too_old_for_pep_604(db, index, &mut diagnostic); + } +} + +pub(super) fn note_py_version_too_old_for_pep_604<'db>( + db: &'db dyn Db, + index: &SemanticIndex<'db>, + diagnostic: &mut Diagnostic, +) { + diagnostic.info("PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted"); + if index.has_future_annotations() { + diagnostic + .info("`from __future__ import annotations` has no effect outside type annotations"); } + add_inferred_python_version_hint_to_diagnostic(db, diagnostic, "resolving types"); } #[derive(Debug, Copy, Clone)] diff --git a/crates/ty_python_semantic/src/types/infer/builder/binary_expressions.rs b/crates/ty_python_semantic/src/types/infer/builder/binary_expressions.rs index 6c6e4b6147a095..bf05e80d713356 100644 --- a/crates/ty_python_semantic/src/types/infer/builder/binary_expressions.rs +++ b/crates/ty_python_semantic/src/types/infer/builder/binary_expressions.rs @@ -48,7 +48,14 @@ impl<'db> TypeInferenceBuilder<'db, '_> { self.infer_binary_expression_type(binary.into(), false, left_ty, right_ty, *op) .unwrap_or_else(|| { - report_unsupported_binary_operation(&self.context, binary, left_ty, right_ty, *op); + report_unsupported_binary_operation( + &self.context, + self.index, + binary, + left_ty, + right_ty, + *op, + ); Type::unknown() }) } diff --git a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs index d1f2e4be3e5766..907b5e755475b7 100644 --- a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs +++ b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs @@ -5,7 +5,8 @@ use super::{DeferredExpressionState, TypeInferenceBuilder}; use crate::semantic_index::scope::ScopeKind; use crate::types::diagnostic::{ self, INVALID_TYPE_FORM, NOT_SUBSCRIPTABLE, UNBOUND_TYPE_VARIABLE, UNSUPPORTED_OPERATOR, - report_invalid_argument_number_to_special_form, report_invalid_arguments_to_callable, + note_py_version_too_old_for_pep_604, report_invalid_argument_number_to_special_form, + report_invalid_arguments_to_callable, }; use crate::types::infer::InferenceFlags; use crate::types::infer::builder::{InnerExpressionInferenceState, MultiInferenceState}; @@ -264,14 +265,10 @@ impl<'db> TypeInferenceBuilder<'db, '_> { && !binary.left.is_string_literal_expr() && !binary.right.is_string_literal_expr() { - diagnostic.info( - "PEP 604 `|` unions are only available on \ - Python 3.10+ unless they are quoted", - ); - add_inferred_python_version_hint_to_diagnostic( + note_py_version_too_old_for_pep_604( self.db(), + self.index, &mut diagnostic, - "inferring types", ); } else if python_version < PythonVersion::PY314 { diagnostic.info(