Skip to content

Commit b1a25fb

Browse files
committed
Fix string_lit_as_bytes lint for macros
Prior to this change, string_lit_as_bytes would trigger for constructs like `include_str!("filename").as_bytes()` and would recommend fixing it by rewriting as `binclude_str!("filename")`. This change updates the lint to act as an EarlyLintPass lint. It then differentiates between string literals and macros that have bytes yielding alternatives. Closes #3205
1 parent 417cf20 commit b1a25fb

File tree

6 files changed

+70
-40
lines changed

6 files changed

+70
-40
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ mod reexport {
200200
pub fn register_pre_expansion_lints(session: &rustc::session::Session, store: &mut rustc::lint::LintStore, conf: &Conf) {
201201
store.register_pre_expansion_pass(Some(session), box write::Pass);
202202
store.register_pre_expansion_pass(Some(session), box redundant_field_names::RedundantFieldNames);
203+
store.register_pre_expansion_pass(Some(session), box strings::StringLitAsBytes);
203204
store.register_pre_expansion_pass(Some(session), box non_expressive_names::NonExpressiveNames {
204205
single_char_binding_names_threshold: conf.single_char_binding_names_threshold,
205206
});
@@ -355,7 +356,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
355356
reg.register_late_lint_pass(box escape::Pass{too_large_for_stack: conf.too_large_for_stack});
356357
reg.register_early_lint_pass(box misc_early::MiscEarly);
357358
reg.register_late_lint_pass(box panic_unimplemented::Pass);
358-
reg.register_late_lint_pass(box strings::StringLitAsBytes);
359359
reg.register_late_lint_pass(box derive::Derive);
360360
reg.register_late_lint_pass(box types::CharLitAsU8);
361361
reg.register_late_lint_pass(box vec::Pass);

clippy_lints/src/strings.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use crate::rustc::hir::*;
2-
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
2+
use crate::rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintPass};
33
use crate::rustc::{declare_tool_lint, lint_array};
44
use crate::syntax::source_map::Spanned;
5-
use crate::utils::SpanlessEq;
6-
use crate::utils::{get_parent_expr, is_allowed, match_type, paths, span_lint, span_lint_and_sugg, walk_ptrs_ty};
5+
use crate::utils::{
6+
get_parent_expr, is_allowed, match_type, paths, span_lint, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq,
7+
};
78

89
/// **What it does:** Checks for string appends of the form `x = x + y` (without
910
/// `let`!).
@@ -82,7 +83,14 @@ impl LintPass for StringAdd {
8283

8384
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd {
8485
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
85-
if let ExprKind::Binary(Spanned { node: BinOpKind::Add, .. }, ref left, _) = e.node {
86+
if let ExprKind::Binary(
87+
Spanned {
88+
node: BinOpKind::Add, ..
89+
},
90+
ref left,
91+
_,
92+
) = e.node
93+
{
8694
if is_string(cx, left) {
8795
if !is_allowed(cx, STRING_ADD_ASSIGN, e.id) {
8896
let parent = get_parent_expr(cx, e);
@@ -122,13 +130,15 @@ fn is_string(cx: &LateContext<'_, '_>, e: &Expr) -> bool {
122130

123131
fn is_add(cx: &LateContext<'_, '_>, src: &Expr, target: &Expr) -> bool {
124132
match src.node {
125-
ExprKind::Binary(Spanned { node: BinOpKind::Add, .. }, ref left, _) => SpanlessEq::new(cx).eq_expr(target, left),
133+
ExprKind::Binary(
134+
Spanned {
135+
node: BinOpKind::Add, ..
136+
},
137+
ref left,
138+
_,
139+
) => SpanlessEq::new(cx).eq_expr(target, left),
126140
ExprKind::Block(ref block, _) => {
127-
block.stmts.is_empty()
128-
&& block
129-
.expr
130-
.as_ref()
131-
.map_or(false, |expr| is_add(cx, expr, target))
141+
block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
132142
},
133143
_ => false,
134144
}
@@ -143,12 +153,12 @@ impl LintPass for StringLitAsBytes {
143153
}
144154
}
145155

146-
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
147-
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
148-
use crate::syntax::ast::LitKind;
156+
impl EarlyLintPass for StringLitAsBytes {
157+
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &crate::syntax::ast::Expr) {
158+
use crate::syntax::ast::{ExprKind, LitKind};
149159
use crate::utils::{in_macro, snippet};
150160

151-
if let ExprKind::MethodCall(ref path, _, ref args) = e.node {
161+
if let ExprKind::MethodCall(ref path, ref args) = e.node {
152162
if path.ident.name == "as_bytes" {
153163
if let ExprKind::Lit(ref lit) = args[0].node {
154164
if let LitKind::Str(ref lit_content, _) = lit.node {
@@ -163,6 +173,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
163173
);
164174
}
165175
}
176+
} else if let ExprKind::Mac(ref mac) = args[0].node {
177+
if mac.node.path == "include_str" {
178+
span_lint_and_sugg(
179+
cx,
180+
STRING_LIT_AS_BYTES,
181+
e.span,
182+
"calling `as_bytes()` on a include_str macro",
183+
"consider using include_bytes instead",
184+
format!("include_bytes!({})", mac.node.stream()),
185+
);
186+
}
166187
}
167188
}
168189
}

tests/ui/strings.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,6 @@ fn both() {
4444
assert_eq!(&x, &z);
4545
}
4646

47-
#[allow(dead_code, unused_variables)]
48-
#[warn(clippy::string_lit_as_bytes)]
49-
fn str_lit_as_bytes() {
50-
let bs = "hello there".as_bytes();
51-
52-
// no warning, because this cannot be written as a byte string literal:
53-
let ubs = "☃".as_bytes();
54-
55-
let strify = stringify!(foobar).as_bytes();
56-
}
57-
5847
fn main() {
5948
add_only();
6049
add_assign_only();

tests/ui/strings.stderr

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,6 @@ error: you added something to a string. Consider using `String::push_str()` inst
5252
42 | let z = y + "...";
5353
| ^^^^^^^^^
5454

55-
error: calling `as_bytes()` on a string literal
56-
--> $DIR/strings.rs:50:14
57-
|
58-
50 | let bs = "hello there".as_bytes();
59-
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"hello there"`
60-
|
61-
= note: `-D clippy::string-lit-as-bytes` implied by `-D warnings`
62-
63-
error: calling `as_bytes()` on a string literal
64-
--> $DIR/strings.rs:55:18
65-
|
66-
55 | let strify = stringify!(foobar).as_bytes();
67-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `bstringify!(foobar)`
68-
6955
error: manual implementation of an assign operation
7056
--> $DIR/strings.rs:65:7
7157
|

tests/ui/strings_early.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(tool_lints)]
2+
3+
#[allow(dead_code, unused_variables)]
4+
#[warn(clippy::string_lit_as_bytes)]
5+
fn str_lit_as_bytes() {
6+
let bs = "hello there".as_bytes();
7+
8+
// no warning, because this cannot be written as a byte string literal:
9+
let ubs = "☃".as_bytes();
10+
11+
let strify = stringify!(foobar).as_bytes();
12+
13+
let includestr = include_str!("entry.rs").as_bytes();
14+
}
15+
16+
fn main() {
17+
assert_eq!(2, 2);
18+
}

tests/ui/strings_early.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: calling `as_bytes()` on a string literal
2+
--> $DIR/strings_early.rs:6:14
3+
|
4+
6 | let bs = "hello there".as_bytes();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"hello there"`
6+
|
7+
= note: `-D clippy::string-lit-as-bytes` implied by `-D warnings`
8+
9+
error: calling `as_bytes()` on a include_str macro
10+
--> $DIR/strings_early.rs:13:22
11+
|
12+
13 | let includestr = include_str!("entry.rs").as_bytes();
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using include_bytes instead: `include_bytes!("entry.rs")`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)