diff --git a/crates/ruff_linter/src/checkers/ast/annotation.rs b/crates/ruff_linter/src/checkers/ast/annotation.rs index 86d1ba50f8..f545ba27eb 100644 --- a/crates/ruff_linter/src/checkers/ast/annotation.rs +++ b/crates/ruff_linter/src/checkers/ast/annotation.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::StmtFunctionDef; +use ruff_python_ast::{PythonVersion, StmtFunctionDef}; use ruff_python_semantic::{ScopeKind, SemanticModel}; use crate::rules::flake8_type_checking; @@ -29,7 +29,11 @@ pub(super) enum AnnotationContext { impl AnnotationContext { /// Determine the [`AnnotationContext`] for an annotation based on the current scope of the /// semantic model. - pub(super) fn from_model(semantic: &SemanticModel, settings: &LinterSettings) -> Self { + pub(super) fn from_model( + semantic: &SemanticModel, + settings: &LinterSettings, + version: PythonVersion, + ) -> Self { // If the annotation is in a class scope (e.g., an annotated assignment for a // class field) or a function scope, and that class or function is marked as // runtime-required, treat the annotation as runtime-required. @@ -59,7 +63,7 @@ impl AnnotationContext { // If `__future__` annotations are enabled or it's a stub file, // then annotations are never evaluated at runtime, // so we can treat them as typing-only. - if semantic.future_annotations_or_stub() { + if semantic.future_annotations_or_stub() || version.defers_annotations() { return Self::TypingOnly; } @@ -81,6 +85,7 @@ impl AnnotationContext { function_def: &StmtFunctionDef, semantic: &SemanticModel, settings: &LinterSettings, + version: PythonVersion, ) -> Self { if flake8_type_checking::helpers::runtime_required_function( function_def, @@ -88,7 +93,7 @@ impl AnnotationContext { semantic, ) { Self::RuntimeRequired - } else if semantic.future_annotations_or_stub() { + } else if semantic.future_annotations_or_stub() || version.defers_annotations() { Self::TypingOnly } else { Self::RuntimeEvaluated diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 2a5a8f3a2b..819e28fdc0 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1004,9 +1004,13 @@ impl<'a> Visitor<'a> for Checker<'a> { } // Function annotations are always evaluated at runtime, unless future annotations - // are enabled. - let annotation = - AnnotationContext::from_function(function_def, &self.semantic, self.settings); + // are enabled or the Python version is at least 3.14. + let annotation = AnnotationContext::from_function( + function_def, + &self.semantic, + self.settings, + self.target_version(), + ); // The first parameter may be a single dispatch. let singledispatch = @@ -1203,7 +1207,11 @@ impl<'a> Visitor<'a> for Checker<'a> { value, .. }) => { - match AnnotationContext::from_model(&self.semantic, self.settings) { + match AnnotationContext::from_model( + &self.semantic, + self.settings, + self.target_version(), + ) { AnnotationContext::RuntimeRequired => { self.visit_runtime_required_annotation(annotation); } @@ -1358,7 +1366,7 @@ impl<'a> Visitor<'a> for Checker<'a> { // we can't defer again, or we'll infinitely recurse! && !self.semantic.in_deferred_type_definition() && self.semantic.in_type_definition() - && self.semantic.future_annotations_or_stub() + && (self.semantic.future_annotations_or_stub()||self.target_version.defers_annotations()) && (self.semantic.in_annotation() || self.source_type.is_stub()) { if let Expr::StringLiteral(string_literal) = expr { @@ -2585,7 +2593,8 @@ impl<'a> Checker<'a> { // if they are annotations in a module where `from __future__ import // annotations` is active, or they are type definitions in a stub file. debug_assert!( - self.semantic.future_annotations_or_stub() + (self.semantic.future_annotations_or_stub() + || self.target_version.defers_annotations()) && (self.source_type.is_stub() || self.semantic.in_annotation()) ); diff --git a/crates/ruff_python_ast/src/python_version.rs b/crates/ruff_python_ast/src/python_version.rs index 20906a2510..a9add9d5a4 100644 --- a/crates/ruff_python_ast/src/python_version.rs +++ b/crates/ruff_python_ast/src/python_version.rs @@ -73,6 +73,10 @@ impl PythonVersion { pub fn supports_pep_701(self) -> bool { self >= Self::PY312 } + + pub fn defers_annotations(self) -> bool { + self >= Self::PY314 + } } impl Default for PythonVersion {