-
Notifications
You must be signed in to change notification settings - Fork 903
/
type_alias_naming.rs
133 lines (120 loc) · 3.71 KB
/
type_alias_naming.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use ruff_python_ast::{self as ast, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for type aliases that do not use the CamelCase naming convention.
///
/// ## Why is this bad?
/// It's conventional to use the CamelCase naming convention for type aliases,
/// to distinguish them from other variables.
///
/// ## Example
/// ```python
/// type_alias_name: TypeAlias = int
/// ```
///
/// Use instead:
/// ```python
/// TypeAliasName: TypeAlias = int
/// ```
#[violation]
pub struct SnakeCaseTypeAlias {
name: String,
}
impl Violation for SnakeCaseTypeAlias {
#[derive_message_formats]
fn message(&self) -> String {
let Self { name } = self;
format!("Type alias `{name}` should be CamelCase")
}
}
/// ## What it does
/// Checks for private type alias definitions suffixed with 'T'.
///
/// ## Why is this bad?
/// It's conventional to use the 'T' suffix for type variables; the use of
/// such a suffix implies that the object is a `TypeVar`.
///
/// Adding the 'T' suffix to a non-`TypeVar`, it can be misleading and should
/// be avoided.
///
/// ## Example
/// ```python
/// from typing import TypeAlias
///
/// _MyTypeT: TypeAlias = int
/// ```
///
/// Use instead:
/// ```python
/// from typing import TypeAlias
///
/// _MyType: TypeAlias = int
/// ```
///
/// ## References
/// - [PEP 484: Type Aliases](https://peps.python.org/pep-0484/#type-aliases)
#[violation]
pub struct TSuffixedTypeAlias {
name: String,
}
impl Violation for TSuffixedTypeAlias {
#[derive_message_formats]
fn message(&self) -> String {
let Self { name } = self;
format!("Private type alias `{name}` should not be suffixed with `T` (the `T` suffix implies that an object is a `TypeVar`)")
}
}
/// Return `true` if the given name is a `snake_case` type alias. In this context, we match against
/// any name that begins with an optional underscore, followed by at least one lowercase letter.
fn is_snake_case_type_alias(name: &str) -> bool {
let mut chars = name.chars();
matches!(
(chars.next(), chars.next()),
(Some('_'), Some('0'..='9' | 'a'..='z')) | (Some('0'..='9' | 'a'..='z'), ..)
)
}
/// Return `true` if the given name is a T-suffixed type alias. In this context, we match against
/// any name that begins with an underscore, and ends in a lowercase letter, followed by `T`,
/// followed by an optional digit.
fn is_t_suffixed_type_alias(name: &str) -> bool {
// A T-suffixed, private type alias must begin with an underscore.
if !name.starts_with('_') {
return false;
}
// It must end in a lowercase letter, followed by `T`, and (optionally) a digit.
let mut chars = name.chars().rev();
matches!(
(chars.next(), chars.next(), chars.next()),
(Some('0'..='9'), Some('T'), Some('a'..='z')) | (Some('T'), Some('a'..='z'), _)
)
}
/// PYI042
pub(crate) fn snake_case_type_alias(checker: &mut Checker, target: &Expr) {
if let Expr::Name(ast::ExprName { id, range, .. }) = target {
if !is_snake_case_type_alias(id) {
return;
}
checker.diagnostics.push(Diagnostic::new(
SnakeCaseTypeAlias {
name: id.to_string(),
},
*range,
));
}
}
/// PYI043
pub(crate) fn t_suffixed_type_alias(checker: &mut Checker, target: &Expr) {
if let Expr::Name(ast::ExprName { id, range, .. }) = target {
if !is_t_suffixed_type_alias(id) {
return;
}
checker.diagnostics.push(Diagnostic::new(
TSuffixedTypeAlias {
name: id.to_string(),
},
*range,
));
}
}