Skip to content

Commit 422d459

Browse files
committed
clippy_dev: Capture token patterns by position and length.
1 parent 5e36990 commit 422d459

File tree

4 files changed

+56
-51
lines changed

4 files changed

+56
-51
lines changed

clippy_dev/src/new_lint.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::parse::cursor::{self, Cursor};
1+
use crate::parse::cursor::{self, Capture, Cursor};
22
use crate::utils::Version;
33
use clap::ValueEnum;
44
use indoc::{formatdoc, writedoc};
@@ -522,6 +522,7 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) {
522522
let mut context = None;
523523
let mut decl_end = None;
524524
let mut cursor = Cursor::new(contents);
525+
let mut captures = [Capture::EMPTY];
525526
while let Some(name) = cursor.find_capture_pat(CaptureIdent) {
526527
match name {
527528
"declare_clippy_lint" => {
@@ -530,9 +531,8 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) {
530531
}
531532
},
532533
"impl" => {
533-
let mut capture = "";
534-
if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) {
535-
match capture {
534+
if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) {
535+
match cursor.get_text(captures[0]) {
536536
"LateLintPass" => context = Some("LateContext"),
537537
"EarlyLintPass" => context = Some("EarlyContext"),
538538
_ => {},

clippy_dev/src/parse.rs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub mod cursor;
22

3-
use self::cursor::Cursor;
3+
use self::cursor::{Capture, Cursor};
44
use crate::utils::{ErrAction, File, expect_action};
55
use core::range::Range;
66
use std::fs;
@@ -102,13 +102,13 @@ fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mu
102102
];
103103

104104
let mut cursor = Cursor::new(contents);
105+
let mut captures = [Capture::EMPTY; 2];
105106
while cursor.find_pat(Ident("declare_clippy_lint")) {
106107
let start = cursor.pos() as usize - "declare_clippy_lint".len();
107-
let (mut name, mut group) = ("", "");
108-
if cursor.match_all(DECL_TOKENS, &mut [&mut name, &mut group]) && cursor.find_pat(CloseBrace) {
108+
if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) {
109109
lints.push(Lint {
110-
name: name.to_lowercase(),
111-
group: group.into(),
110+
name: cursor.get_text(captures[0]).to_lowercase(),
111+
group: cursor.get_text(captures[1]).into(),
112112
module: module.into(),
113113
path: path.into(),
114114
declaration_range: start..cursor.pos() as usize,
@@ -146,6 +146,7 @@ pub fn read_deprecated_lints() -> (Vec<DeprecatedLint>, Vec<RenamedLint>) {
146146
File::open_read_to_cleared_string(path, &mut contents);
147147

148148
let mut cursor = Cursor::new(&contents);
149+
let mut captures = [Capture::EMPTY; 3];
149150

150151
// First instance is the macro definition.
151152
assert!(
@@ -154,29 +155,23 @@ pub fn read_deprecated_lints() -> (Vec<DeprecatedLint>, Vec<RenamedLint>) {
154155
);
155156

156157
if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(DEPRECATED_TOKENS, &mut []) {
157-
let mut version = "";
158-
let mut name = "";
159-
let mut reason = "";
160-
while cursor.match_all(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) {
158+
while cursor.match_all(DECL_TOKENS, &mut captures) {
161159
deprecated.push(DeprecatedLint {
162-
name: parse_str_single_line(path.as_ref(), name),
163-
reason: parse_str_single_line(path.as_ref(), reason),
164-
version: parse_str_single_line(path.as_ref(), version),
160+
name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])),
161+
reason: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])),
162+
version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])),
165163
});
166164
}
167165
} else {
168166
panic!("error reading deprecated lints");
169167
}
170168

171169
if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(RENAMED_TOKENS, &mut []) {
172-
let mut version = "";
173-
let mut old_name = "";
174-
let mut new_name = "";
175-
while cursor.match_all(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) {
170+
while cursor.match_all(DECL_TOKENS, &mut captures) {
176171
renamed.push(RenamedLint {
177-
old_name: parse_str_single_line(path.as_ref(), old_name),
178-
new_name: parse_str_single_line(path.as_ref(), new_name),
179-
version: parse_str_single_line(path.as_ref(), version),
172+
old_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])),
173+
new_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])),
174+
version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])),
180175
});
181176
}
182177
} else {

clippy_dev/src/parse/cursor.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ pub enum Pat<'a> {
3232
Semi,
3333
}
3434

35+
#[derive(Clone, Copy)]
36+
pub struct Capture {
37+
pub pos: u32,
38+
pub len: u32,
39+
}
40+
impl Capture {
41+
pub const EMPTY: Self = Self { pos: 0, len: 0 };
42+
}
43+
3544
/// A unidirectional cursor over a token stream that is lexed on demand.
3645
pub struct Cursor<'txt> {
3746
next_token: Token,
@@ -51,6 +60,12 @@ impl<'txt> Cursor<'txt> {
5160
}
5261
}
5362

63+
/// Gets the text of the captured token assuming it came from this cursor.
64+
#[must_use]
65+
pub fn get_text(&self, capture: Capture) -> &'txt str {
66+
&self.text[capture.pos as usize..(capture.pos + capture.len) as usize]
67+
}
68+
5469
/// Gets the text that makes up the next token in the stream, or the empty string if
5570
/// stream is exhausted.
5671
#[must_use]
@@ -97,7 +112,7 @@ impl<'txt> Cursor<'txt> {
97112
///
98113
/// For each capture made by the pattern one item will be taken from the capture
99114
/// sequence with the result placed inside.
100-
fn match_pat(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool {
115+
fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture>) -> bool {
101116
loop {
102117
match (pat, self.next_token.kind) {
103118
#[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/6697
@@ -154,7 +169,7 @@ impl<'txt> Cursor<'txt> {
154169
},
155170
)
156171
| (Pat::CaptureIdent, TokenKind::Ident) => {
157-
**captures.next().unwrap() = self.peek_text();
172+
*captures.next().unwrap() = Capture { pos: self.pos, len: self.next_token.len };
158173
self.step();
159174
return true;
160175
},
@@ -169,9 +184,9 @@ impl<'txt> Cursor<'txt> {
169184
/// Not generally suitable for multi-token patterns or patterns that can match
170185
/// nothing.
171186
#[must_use]
172-
pub fn find_pat(&mut self, token: Pat<'_>) -> bool {
187+
pub fn find_pat(&mut self, pat: Pat<'_>) -> bool {
173188
let mut capture = [].iter_mut();
174-
while !self.match_pat(token, &mut capture) {
189+
while !self.match_impl(pat, &mut capture) {
175190
self.step();
176191
if self.at_end() {
177192
return false;
@@ -182,21 +197,20 @@ impl<'txt> Cursor<'txt> {
182197

183198
/// The same as [`Self::find_pat`], but returns a capture as well.
184199
#[must_use]
185-
pub fn find_capture_pat(&mut self, token: Pat<'_>) -> Option<&'txt str> {
186-
let mut res = "";
187-
let mut capture = &mut res;
188-
let mut capture = slice::from_mut(&mut capture).iter_mut();
189-
while !self.match_pat(token, &mut capture) {
200+
pub fn find_capture_pat(&mut self, pat: Pat<'_>) -> Option<&'txt str> {
201+
let mut capture = Capture::EMPTY;
202+
let mut captures = slice::from_mut(&mut capture).iter_mut();
203+
while !self.match_impl(pat, &mut captures) {
190204
self.step();
191205
if self.at_end() {
192206
return None;
193207
}
194208
}
195-
Some(res)
209+
Some(self.get_text(capture))
196210
}
197211

198212
/// Attempts to match a sequence of patterns at the current position. Returns whether
199-
/// the match is successful.
213+
/// all patterns were successfully matched.
200214
///
201215
/// Captures will be written to the given slice in the order they're matched. If a
202216
/// capture is matched, but there are no more capture slots this will panic. If the
@@ -205,8 +219,8 @@ impl<'txt> Cursor<'txt> {
205219
///
206220
/// If the match fails the cursor will be positioned at the first failing token.
207221
#[must_use]
208-
pub fn match_all(&mut self, tokens: &[Pat<'_>], captures: &mut [&mut &'txt str]) -> bool {
222+
pub fn match_all(&mut self, pats: &[Pat<'_>], captures: &mut [Capture]) -> bool {
209223
let mut captures = captures.iter_mut();
210-
tokens.iter().all(|&t| self.match_pat(t, &mut captures))
224+
pats.iter().all(|&t| self.match_impl(t, &mut captures))
211225
}
212226
}

clippy_dev/src/rename_lint.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::parse::cursor::{self, Cursor};
1+
use crate::parse::cursor::{self, Capture, Cursor};
22
use crate::parse::{RenamedLint, find_lint_decls, read_deprecated_lints};
33
use crate::update_lints::generate_lint_files;
44
use crate::utils::{
@@ -281,7 +281,7 @@ fn file_update_fn<'a, 'b>(
281281
let mut copy_pos = 0u32;
282282
let mut changed = false;
283283
let mut cursor = Cursor::new(src);
284-
let mut capture = "";
284+
let mut captures = [Capture::EMPTY];
285285
loop {
286286
match cursor.peek() {
287287
TokenKind::Eof => break,
@@ -292,12 +292,10 @@ fn file_update_fn<'a, 'b>(
292292
match text {
293293
// clippy::line_name or clippy::lint-name
294294
"clippy" => {
295-
if cursor.match_all(
296-
&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent],
297-
&mut [&mut capture],
298-
) && capture == old_name
295+
if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures)
296+
&& cursor.get_text(captures[0]) == old_name
299297
{
300-
dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]);
298+
dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]);
301299
dst.push_str(new_name);
302300
copy_pos = cursor.pos();
303301
changed = true;
@@ -306,12 +304,12 @@ fn file_update_fn<'a, 'b>(
306304
// mod lint_name
307305
"mod" => {
308306
if !matches!(mod_edit, ModEdit::None)
309-
&& cursor.match_all(&[cursor::Pat::CaptureIdent], &mut [&mut capture])
310-
&& capture == old_name
307+
&& cursor.match_all(&[cursor::Pat::CaptureIdent], &mut captures)
308+
&& cursor.get_text(captures[0]) == old_name
311309
{
312310
match mod_edit {
313311
ModEdit::Rename => {
314-
dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]);
312+
dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]);
315313
dst.push_str(new_name);
316314
copy_pos = cursor.pos();
317315
changed = true;
@@ -374,12 +372,10 @@ fn file_update_fn<'a, 'b>(
374372
},
375373
// ::lint_name
376374
TokenKind::Colon
377-
if cursor.match_all(
378-
&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent],
379-
&mut [&mut capture],
380-
) && capture == old_name =>
375+
if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures)
376+
&& cursor.get_text(captures[0]) == old_name =>
381377
{
382-
dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]);
378+
dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]);
383379
dst.push_str(new_name);
384380
copy_pos = cursor.pos();
385381
changed = true;

0 commit comments

Comments
 (0)