[ty] Document `invalid-syntax-in-forward-annotation` and `escape-character-in-forward-annotation` (#22130)

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
Will Duke 2025-12-21 19:35:44 +00:00 committed by GitHub
parent b4c2825afd
commit b6e84eca16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 153 additions and 58 deletions

View File

@ -380,11 +380,24 @@ class A: # Crash at runtime
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20escape-character-in-forward-annotation" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L120" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L154" target="_blank">View source</a>
</small>
TODO #14889
**What it does**
Checks for forward annotations that contain escape characters.
**Why is this bad?**
Static analysis tools like ty can't analyze type annotations that contain escape characters.
**Example**
```python
def foo() -> "intt\b": ...
```
## `fstring-type-annotation`
@ -1597,7 +1610,45 @@ Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.
</small>
TODO #14889
**What it does**
Checks for string-literal annotations where the string cannot be
parsed as a Python expression.
**Why is this bad?**
Type annotations are expected to be Python expressions that
describe the expected type of a variable, parameter, attribute or
`return` statement.
Type annotations are permitted to be string-literal expressions, in
order to enable forward references to names not yet defined.
However, it must be possible to parse the contents of that string
literal as a normal Python expression.
**Example**
```python
def foo() -> "intstance of C":
return 42
class C: ...
```
Use instead:
```python
def foo() -> "C":
return 42
class C: ...
```
**References**
- [Typing spec: The meaning of annotations](https://typing.python.org/en/latest/spec/annotations.html#the-meaning-of-annotations)
- [Typing spec: String annotations](https://typing.python.org/en/latest/spec/annotations.html#string-annotations)
## `invalid-type-alias-type`

View File

@ -34,23 +34,23 @@ declare_lint! {
}
declare_lint! {
/// ## What it does
/// Checks for byte-strings in type annotation positions.
///
/// ## Why is this bad?
/// Static analysis tools like ty can't analyze type annotations that use byte-string notation.
///
/// ## Examples
/// ```python
/// def test(): -> b"int":
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def test(): -> "int":
/// ...
/// ```
/// ## What it does
/// Checks for byte-strings in type annotation positions.
///
/// ## Why is this bad?
/// Static analysis tools like ty can't analyze type annotations that use byte-string notation.
///
/// ## Examples
/// ```python
/// def test(): -> b"int":
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def test(): -> "int":
/// ...
/// ```
pub(crate) static BYTE_STRING_TYPE_ANNOTATION = {
summary: "detects byte strings in type annotation positions",
status: LintStatus::stable("0.0.1-alpha.1"),
@ -59,23 +59,23 @@ declare_lint! {
}
declare_lint! {
/// ## What it does
/// Checks for raw-strings in type annotation positions.
///
/// ## Why is this bad?
/// Static analysis tools like ty can't analyze type annotations that use raw-string notation.
///
/// ## Examples
/// ```python
/// def test(): -> r"int":
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def test(): -> "int":
/// ...
/// ```
/// ## What it does
/// Checks for raw-strings in type annotation positions.
///
/// ## Why is this bad?
/// Static analysis tools like ty can't analyze type annotations that use raw-string notation.
///
/// ## Examples
/// ```python
/// def test(): -> r"int":
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def test(): -> "int":
/// ...
/// ```
pub(crate) static RAW_STRING_TYPE_ANNOTATION = {
summary: "detects raw strings in type annotation positions",
status: LintStatus::stable("0.0.1-alpha.1"),
@ -84,23 +84,23 @@ declare_lint! {
}
declare_lint! {
/// ## What it does
/// Checks for implicit concatenated strings in type annotation positions.
///
/// ## Why is this bad?
/// Static analysis tools like ty can't analyze type annotations that use implicit concatenated strings.
///
/// ## Examples
/// ```python
/// def test(): -> "Literal[" "5" "]":
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def test(): -> "Literal[5]":
/// ...
/// ```
/// ## What it does
/// Checks for implicit concatenated strings in type annotation positions.
///
/// ## Why is this bad?
/// Static analysis tools like ty can't analyze type annotations that use implicit concatenated strings.
///
/// ## Examples
/// ```python
/// def test(): -> "Literal[" "5" "]":
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def test(): -> "Literal[5]":
/// ...
/// ```
pub(crate) static IMPLICIT_CONCATENATED_STRING_TYPE_ANNOTATION = {
summary: "detects implicit concatenated strings in type annotations",
status: LintStatus::stable("0.0.1-alpha.1"),
@ -109,7 +109,41 @@ declare_lint! {
}
declare_lint! {
/// TODO #14889
/// ## What it does
/// Checks for string-literal annotations where the string cannot be
/// parsed as a Python expression.
///
/// ## Why is this bad?
/// Type annotations are expected to be Python expressions that
/// describe the expected type of a variable, parameter, attribute or
/// `return` statement.
///
/// Type annotations are permitted to be string-literal expressions, in
/// order to enable forward references to names not yet defined.
/// However, it must be possible to parse the contents of that string
/// literal as a normal Python expression.
///
/// ## Example
///
/// ```python
/// def foo() -> "intstance of C":
/// return 42
///
/// class C: ...
/// ```
///
/// Use instead:
///
/// ```python
/// def foo() -> "C":
/// return 42
///
/// class C: ...
/// ```
///
/// ## References
/// - [Typing spec: The meaning of annotations](https://typing.python.org/en/latest/spec/annotations.html#the-meaning-of-annotations)
/// - [Typing spec: String annotations](https://typing.python.org/en/latest/spec/annotations.html#string-annotations)
pub(crate) static INVALID_SYNTAX_IN_FORWARD_ANNOTATION = {
summary: "detects invalid syntax in forward annotations",
status: LintStatus::stable("0.0.1-alpha.1"),
@ -118,7 +152,17 @@ declare_lint! {
}
declare_lint! {
/// TODO #14889
/// ## What it does
/// Checks for forward annotations that contain escape characters.
///
/// ## Why is this bad?
/// Static analysis tools like ty can't analyze type annotations that contain escape characters.
///
/// ## Example
///
/// ```python
/// def foo() -> "intt\b": ...
/// ```
pub(crate) static ESCAPE_CHARACTER_IN_FORWARD_ANNOTATION = {
summary: "detects forward type annotations with escape characters",
status: LintStatus::stable("0.0.1-alpha.1"),

4
ty.schema.json generated
View File

@ -425,7 +425,7 @@
},
"escape-character-in-forward-annotation": {
"title": "detects forward type annotations with escape characters",
"description": "TODO #14889",
"description": "## What it does\nChecks for forward annotations that contain escape characters.\n\n## Why is this bad?\nStatic analysis tools like ty can't analyze type annotations that contain escape characters.\n\n## Example\n\n```python\ndef foo() -> \"intt\\b\": ...\n```",
"default": "error",
"oneOf": [
{
@ -745,7 +745,7 @@
},
"invalid-syntax-in-forward-annotation": {
"title": "detects invalid syntax in forward annotations",
"description": "TODO #14889",
"description": "## What it does\nChecks for string-literal annotations where the string cannot be\nparsed as a Python expression.\n\n## Why is this bad?\nType annotations are expected to be Python expressions that\ndescribe the expected type of a variable, parameter, attribute or\n`return` statement.\n\nType annotations are permitted to be string-literal expressions, in\norder to enable forward references to names not yet defined.\nHowever, it must be possible to parse the contents of that string\nliteral as a normal Python expression.\n\n## Example\n\n```python\ndef foo() -> \"intstance of C\":\n return 42\n\nclass C: ...\n```\n\nUse instead:\n\n```python\ndef foo() -> \"C\":\n return 42\n\nclass C: ...\n```\n\n## References\n- [Typing spec: The meaning of annotations](https://typing.python.org/en/latest/spec/annotations.html#the-meaning-of-annotations)\n- [Typing spec: String annotations](https://typing.python.org/en/latest/spec/annotations.html#string-annotations)",
"default": "error",
"oneOf": [
{