mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-17 00:50:16 +00:00
Allow flake8-type-checking
rules to automatically quote runtime-evaluated references (#6001)
## Summary This allows us to fix usages like: ```python from pandas import DataFrame def baz() -> DataFrame: ... ``` By quoting the `DataFrame` in `-> DataFrame`. Without quotes, moving `from pandas import DataFrame` into an `if TYPE_CHECKING:` block will fail at runtime, since Python tries to evaluate the annotation to add it to the function's `__annotations__`. Unfortunately, this does require us to split our "annotation kind" flags into three categories, rather than two: - `typing-only`: The annotation is only evaluated at type-checking-time. - `runtime-evaluated`: Python will evaluate the annotation at runtime (like above) -- but we're willing to quote it. - `runtime-required`: Python will evaluate the annotation at runtime (like above), and some library (like Pydantic) needs it to be available at runtime, so we _can't_ quote it. This functionality is gated behind a setting (`flake8-type-checking.quote-annotations`). Closes https://github.com/astral-sh/ruff/issues/5559.
This commit is contained in:
parent
4d2ee5bf98
commit
1a65e544c5
18 changed files with 1034 additions and 208 deletions
|
@ -8,11 +8,14 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
|
||||
use crate::context::ExecutionContext;
|
||||
use crate::scope::ScopeId;
|
||||
use crate::{Exceptions, SemanticModelFlags};
|
||||
use crate::{Exceptions, NodeId, SemanticModelFlags};
|
||||
|
||||
/// A resolved read reference to a name in a program.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ResolvedReference {
|
||||
/// The expression that the reference occurs in. `None` if the reference is a global
|
||||
/// reference or a reference via an augmented assignment.
|
||||
node_id: Option<NodeId>,
|
||||
/// The scope in which the reference is defined.
|
||||
scope_id: ScopeId,
|
||||
/// The range of the reference in the source code.
|
||||
|
@ -22,6 +25,11 @@ pub struct ResolvedReference {
|
|||
}
|
||||
|
||||
impl ResolvedReference {
|
||||
/// The expression that the reference occurs in.
|
||||
pub const fn expression_id(&self) -> Option<NodeId> {
|
||||
self.node_id
|
||||
}
|
||||
|
||||
/// The scope in which the reference is defined.
|
||||
pub const fn scope_id(&self) -> ScopeId {
|
||||
self.scope_id
|
||||
|
@ -35,6 +43,48 @@ impl ResolvedReference {
|
|||
ExecutionContext::Runtime
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a typing-only type annotation.
|
||||
pub const fn in_typing_only_annotation(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::TYPING_ONLY_ANNOTATION)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a runtime-required type annotation.
|
||||
pub const fn in_runtime_evaluated_annotation(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::RUNTIME_EVALUATED_ANNOTATION)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a "simple" string type definition.
|
||||
pub const fn in_simple_string_type_definition(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::SIMPLE_STRING_TYPE_DEFINITION)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a "complex" string type definition.
|
||||
pub const fn in_complex_string_type_definition(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::COMPLEX_STRING_TYPE_DEFINITION)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a `__future__` type definition.
|
||||
pub const fn in_future_type_definition(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::FUTURE_TYPE_DEFINITION)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in any kind of deferred type definition.
|
||||
pub const fn in_deferred_type_definition(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::DEFERRED_TYPE_DEFINITION)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a type-checking block.
|
||||
pub const fn in_type_checking_block(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::TYPE_CHECKING_BLOCK)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for ResolvedReference {
|
||||
|
@ -57,10 +107,12 @@ impl ResolvedReferences {
|
|||
pub(crate) fn push(
|
||||
&mut self,
|
||||
scope_id: ScopeId,
|
||||
node_id: Option<NodeId>,
|
||||
range: TextRange,
|
||||
flags: SemanticModelFlags,
|
||||
) -> ResolvedReferenceId {
|
||||
self.0.push(ResolvedReference {
|
||||
node_id,
|
||||
scope_id,
|
||||
range,
|
||||
flags,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue