Skip to content

Commit 846bd30

Browse files
committed
raw_strings: separate raw string detection from lint logic
1 parent 753629b commit 846bd30

File tree

1 file changed

+92
-81
lines changed

1 file changed

+92
-81
lines changed

clippy_lints/src/raw_strings.rs

Lines changed: 92 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -81,94 +81,105 @@ impl EarlyLintPass for RawStrings {
8181
&& !in_external_macro(cx.sess(), expr.span)
8282
&& expr.span.check_source_text(cx, |src| src.starts_with(prefix))
8383
{
84-
let str = lit.symbol.as_str();
85-
let descr = lit.kind.descr();
86-
87-
if !str.contains(['\\', '"']) {
88-
span_lint_and_then(
89-
cx,
90-
NEEDLESS_RAW_STRINGS,
91-
expr.span,
92-
"unnecessary raw string literal",
93-
|diag| {
94-
let (start, end) = hash_spans(expr.span, prefix.len(), 0, max);
95-
96-
// BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
97-
let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1);
98-
let start = start.with_lo(r_pos);
99-
100-
let mut remove = vec![(start, String::new())];
101-
// avoid debug ICE from empty suggestions
102-
if !end.is_empty() {
103-
remove.push((end, String::new()));
104-
}
84+
self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr());
85+
}
86+
}
87+
}
10588

106-
diag.multipart_suggestion_verbose(
107-
format!("use a plain {descr} literal instead"),
108-
remove,
109-
Applicability::MachineApplicable,
110-
);
111-
},
112-
);
113-
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
114-
return;
115-
}
89+
impl RawStrings {
90+
fn check_raw_string(
91+
&mut self,
92+
cx: &EarlyContext<'_>,
93+
str: &str,
94+
lit_span: Span,
95+
prefix: &str,
96+
max: u8,
97+
descr: &str,
98+
) {
99+
if !str.contains(['\\', '"']) {
100+
span_lint_and_then(
101+
cx,
102+
NEEDLESS_RAW_STRINGS,
103+
lit_span,
104+
"unnecessary raw string literal",
105+
|diag| {
106+
let (start, end) = hash_spans(lit_span, prefix.len(), 0, max);
107+
108+
// BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
109+
let r_pos = lit_span.lo() + BytePos::from_usize(prefix.len() - 1);
110+
let start = start.with_lo(r_pos);
111+
112+
let mut remove = vec![(start, String::new())];
113+
// avoid debug ICE from empty suggestions
114+
if !end.is_empty() {
115+
remove.push((end, String::new()));
116+
}
117+
118+
diag.multipart_suggestion_verbose(
119+
format!("use a plain {descr} literal instead"),
120+
remove,
121+
Applicability::MachineApplicable,
122+
);
123+
},
124+
);
125+
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
126+
return;
116127
}
128+
}
117129

118-
let mut req = {
119-
let mut following_quote = false;
120-
let mut req = 0;
121-
// `once` so a raw string ending in hashes is still checked
122-
let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
123-
match b {
124-
b'"' if !following_quote => (following_quote, req) = (true, 1),
125-
b'#' => req += u8::from(following_quote),
126-
_ => {
127-
if following_quote {
128-
following_quote = false;
129-
130-
if req == max {
131-
return ControlFlow::Break(req);
132-
}
133-
134-
return ControlFlow::Continue(acc.max(req));
130+
let mut req = {
131+
let mut following_quote = false;
132+
let mut req = 0;
133+
// `once` so a raw string ending in hashes is still checked
134+
let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
135+
match b {
136+
b'"' if !following_quote => (following_quote, req) = (true, 1),
137+
b'#' => req += u8::from(following_quote),
138+
_ => {
139+
if following_quote {
140+
following_quote = false;
141+
142+
if req == max {
143+
return ControlFlow::Break(req);
135144
}
136-
},
137-
}
138-
139-
ControlFlow::Continue(acc)
140-
});
141145

142-
match num {
143-
ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
144-
}
145-
};
146-
if self.allow_one_hash_in_raw_strings {
147-
req = req.max(1);
148-
}
149-
if req < max {
150-
span_lint_and_then(
151-
cx,
152-
NEEDLESS_RAW_STRING_HASHES,
153-
expr.span,
154-
"unnecessary hashes around raw string literal",
155-
|diag| {
156-
let (start, end) = hash_spans(expr.span, prefix.len(), req, max);
157-
158-
let message = match max - req {
159-
_ if req == 0 => format!("remove all the hashes around the {descr} literal"),
160-
1 => format!("remove one hash from both sides of the {descr} literal"),
161-
n => format!("remove {n} hashes from both sides of the {descr} literal"),
162-
};
163-
164-
diag.multipart_suggestion(
165-
message,
166-
vec![(start, String::new()), (end, String::new())],
167-
Applicability::MachineApplicable,
168-
);
146+
return ControlFlow::Continue(acc.max(req));
147+
}
169148
},
170-
);
149+
}
150+
151+
ControlFlow::Continue(acc)
152+
});
153+
154+
match num {
155+
ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
171156
}
157+
};
158+
if self.allow_one_hash_in_raw_strings {
159+
req = req.max(1);
160+
}
161+
if req < max {
162+
span_lint_and_then(
163+
cx,
164+
NEEDLESS_RAW_STRING_HASHES,
165+
lit_span,
166+
"unnecessary hashes around raw string literal",
167+
|diag| {
168+
let (start, end) = hash_spans(lit_span, prefix.len(), req, max);
169+
170+
let message = match max - req {
171+
_ if req == 0 => format!("remove all the hashes around the {descr} literal"),
172+
1 => format!("remove one hash from both sides of the {descr} literal"),
173+
n => format!("remove {n} hashes from both sides of the {descr} literal"),
174+
};
175+
176+
diag.multipart_suggestion(
177+
message,
178+
vec![(start, String::new()), (end, String::new())],
179+
Applicability::MachineApplicable,
180+
);
181+
},
182+
);
172183
}
173184
}
174185
}

0 commit comments

Comments
 (0)