-
Notifications
You must be signed in to change notification settings - Fork 882
/
native_literals.rs
111 lines (103 loc) · 3.32 KB
/
native_literals.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
use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::registry::{Check, CheckCode, CheckKind, LiteralType};
/// UP018
pub fn native_literals(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let ExprKind::Name { id, .. } = &func.node else { return; };
if !keywords.is_empty() || args.len() > 1 {
return;
}
if (id == "str" || id == "bytes") && checker.is_builtin(id) {
let Some(arg) = args.get(0) else {
let mut check = Check::new(CheckKind::NativeLiterals(if id == "str" {
LiteralType::Str
} else {
LiteralType::Bytes
}), Range::from_located(expr));
if checker.patch(&CheckCode::UP018) {
check.amend(Fix::replacement(
if id == "bytes" {
let mut content = String::with_capacity(3);
content.push('b');
content.push(checker.style.quote().into());
content.push(checker.style.quote().into());
content
} else {
let mut content = String::with_capacity(2);
content.push(checker.style.quote().into());
content.push(checker.style.quote().into());
content
},
expr.location,
expr.end_location.unwrap(),
));
}
checker.add_check(check);
return;
};
// Look for `str("")`.
if id == "str"
&& !matches!(
&arg.node,
ExprKind::Constant {
value: Constant::Str(_),
..
},
)
{
return;
}
// Look for `bytes(b"")`
if id == "bytes"
&& !matches!(
&arg.node,
ExprKind::Constant {
value: Constant::Bytes(_),
..
},
)
{
return;
}
// rust-python merges adjacent string/bytes literals into one node, but we can't
// safely remove the outer call in this situation. We're following pyupgrade
// here and skip.
let arg_code = checker
.locator
.slice_source_code_range(&Range::from_located(arg));
if lexer::make_tokenizer(&arg_code)
.flatten()
.filter(|(_, tok, _)| matches!(tok, Tok::String { .. }))
.count()
> 1
{
return;
}
let mut check = Check::new(
CheckKind::NativeLiterals(if id == "str" {
LiteralType::Str
} else {
LiteralType::Bytes
}),
Range::from_located(expr),
);
if checker.patch(&CheckCode::UP018) {
check.amend(Fix::replacement(
arg_code.to_string(),
expr.location,
expr.end_location.unwrap(),
));
}
checker.add_check(check);
}
}