Skip to content

Commit 8ab97f3

Browse files
committed
clippy_dev: parse lint pass macro calls
1 parent c93dcbd commit 8ab97f3

File tree

2 files changed

+134
-38
lines changed

2 files changed

+134
-38
lines changed

clippy_dev/src/new_lint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) {
524524
let mut decl_end = None;
525525
let mut searcher = RustSearcher::new(contents);
526526
let mut captures = [Capture::EMPTY];
527-
while let Some(name) = searcher.find_capture_token(CaptureIdent) {
527+
while let Some(name) = searcher.find_any_ident() {
528528
match name {
529529
"declare_clippy_lint" => {
530530
if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) {

clippy_dev/src/parse.rs

Lines changed: 133 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::utils::{ErrAction, expect_action, walk_dir_no_dot_or_target};
33
use core::ops::Range;
44
use core::slice;
55
use rustc_data_structures::fx::FxHashMap;
6+
use rustc_index::{IndexVec, newtype_index};
67
use rustc_lexer::{self as lexer, FrontmatterAllowed};
78
use std::collections::hash_map::{Entry, VacantEntry};
89
use std::panic::Location;
@@ -27,13 +28,15 @@ pub enum Token<'a> {
2728
DoubleColon,
2829
Comma,
2930
Eq,
31+
FatArrow,
3032
Lifetime,
3133
Literal,
3234
Lt,
3335
Gt,
3436
OpenBrace,
3537
OpenBracket,
3638
OpenParen,
39+
OptLifetimeArgs,
3740
Pound,
3841
Semi,
3942
}
@@ -202,6 +205,24 @@ impl<'txt> RustSearcher<'txt> {
202205
}
203206
return false;
204207
},
208+
(Token::FatArrow, lexer::TokenKind::Eq) => {
209+
self.step();
210+
if matches!(self.next_token.kind, lexer::TokenKind::Gt) {
211+
self.step();
212+
return true;
213+
}
214+
return false;
215+
},
216+
(Token::OptLifetimeArgs, lexer::TokenKind::Lt) => {
217+
self.step();
218+
while self.read_token(Token::Lifetime, captures) {
219+
if !self.read_token(Token::Comma, captures) {
220+
break;
221+
}
222+
}
223+
return self.read_token(Token::Gt, captures);
224+
},
225+
(Token::OptLifetimeArgs, _) => return true,
205226
#[rustfmt::skip]
206227
(
207228
Token::CaptureLitStr,
@@ -235,16 +256,28 @@ impl<'txt> RustSearcher<'txt> {
235256
}
236257

237258
#[must_use]
238-
pub fn find_capture_token(&mut self, token: Token<'_>) -> Option<&'txt str> {
239-
let mut capture = Capture::EMPTY;
240-
let mut captures = slice::from_mut(&mut capture).iter_mut();
241-
while !self.read_token(token, &mut captures) {
242-
self.step();
243-
if self.at_end() {
244-
return None;
259+
pub fn find_any_ident(&mut self) -> Option<&'txt str> {
260+
loop {
261+
match self.next_token.kind {
262+
lexer::TokenKind::Ident => {
263+
let res = self.peek_text();
264+
self.step();
265+
return Some(res);
266+
},
267+
lexer::TokenKind::Eof => return None,
268+
_ => self.step(),
269+
}
270+
}
271+
}
272+
273+
#[must_use]
274+
pub fn find_ident(&mut self, s: &str) -> bool {
275+
while let Some(x) = self.find_any_ident() {
276+
if x == s {
277+
return true;
245278
}
246279
}
247-
Some(&self.text[capture.to_index()])
280+
false
248281
}
249282

250283
#[must_use]
@@ -286,9 +319,27 @@ pub struct Lint {
286319
pub name_span: Span,
287320
}
288321

322+
pub struct LintPassData {
323+
pub name: String,
324+
/// Span of the `impl_lint_pass` or `declare_lint_pass` macro call.
325+
pub mac_span: Span,
326+
}
327+
328+
newtype_index! {
329+
#[orderable]
330+
pub struct LintPass {}
331+
}
332+
333+
pub struct LintRegistration {
334+
pub name: String,
335+
pub pass: LintPass,
336+
}
337+
289338
pub struct ParsedData {
290339
pub source_map: SourceMap,
291340
pub lints: FxHashMap<String, Lint>,
341+
pub lint_passes: IndexVec<LintPass, LintPassData>,
342+
pub lint_registrations: Vec<LintRegistration>,
292343
pub deprecated_span: Range<u32>,
293344
pub renamed_span: Range<u32>,
294345
}
@@ -298,6 +349,8 @@ impl ParsedData {
298349
let mut parser = Parser {
299350
source_map: SourceMap::with_capacity(8, 1000),
300351
lints: FxHashMap::with_capacity_and_hasher(1000, Default::default()),
352+
lint_passes: IndexVec::with_capacity(400),
353+
lint_registrations: Vec::with_capacity(1000),
301354
deprecated_span: 0..0,
302355
renamed_span: 0..0,
303356
errors: Vec::new(),
@@ -354,6 +407,8 @@ impl ParsedData {
354407
ParsedData {
355408
source_map: parser.source_map,
356409
lints: parser.lints,
410+
lint_passes: parser.lint_passes,
411+
lint_registrations: parser.lint_registrations,
357412
deprecated_span: parser.deprecated_span,
358413
renamed_span: parser.renamed_span,
359414
}
@@ -398,6 +453,8 @@ impl From<ErrorKind> for Error {
398453
struct Parser {
399454
source_map: SourceMap,
400455
lints: FxHashMap<String, Lint>,
456+
lint_passes: IndexVec<LintPass, LintPassData>,
457+
lint_registrations: Vec<LintRegistration>,
401458
deprecated_span: Range<u32>,
402459
renamed_span: Range<u32>,
403460
errors: Vec<Error>,
@@ -448,7 +505,7 @@ impl Parser {
448505
#[allow(clippy::enum_glob_use)]
449506
use Token::*;
450507
#[rustfmt::skip]
451-
static DECL_TOKENS: &[Token<'_>] = &[
508+
static LINT_DECL_TOKENS: &[Token<'_>] = &[
452509
// { /// docs
453510
OpenBrace, AnyComment,
454511
// #[clippy::version = "version"]
@@ -457,41 +514,80 @@ impl Parser {
457514
Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, AnyComment, LitStr,
458515
];
459516
#[rustfmt::skip]
460-
static EXTRA_TOKENS: &[Token<'_>] = &[
517+
static LINT_DECL_EXTRA_TOKENS: &[Token<'_>] = &[
461518
// , @option = value
462519
Comma, AnyComment, At, AnyIdent, Eq, Literal,
463520
];
521+
#[rustfmt::skip]
522+
static LINT_PASS_TOKENS: &[Token<'_>] = &[
523+
// ( name <'lt> => [
524+
OpenParen, AnyComment, CaptureIdent, OptLifetimeArgs, FatArrow, OpenBracket,
525+
];
464526

465527
let mut searcher = RustSearcher::new(&self.source_map.files[file].contents);
466528
let mut captures = [Capture::EMPTY; 2];
467-
while searcher.find_token(Ident("declare_clippy_lint")) {
468-
let start = searcher.pos() - "declare_clippy_lint".len() as u32;
529+
while let Some(ident) = searcher.find_any_ident() {
530+
let start = searcher.pos - ident.len() as u32;
469531
if searcher.match_token(Bang) {
470-
if !searcher.match_tokens(DECL_TOKENS, &mut captures) {
471-
self.errors.push(searcher.get_unexpected_err(file));
472-
return;
473-
}
474-
let name_span = captures[0].to_span(file);
475-
let name = searcher.get_capture(captures[0]).to_ascii_lowercase();
476-
if let Some(e) = get_vacant_lint(name, name_span, &mut self.lints, &mut self.errors) {
477-
e.insert(Lint {
478-
name_span,
479-
kind: LintKind::Active(ActiveLint {
480-
decl_span: Span {
532+
match ident {
533+
"declare_clippy_lint" => {
534+
if !searcher.match_tokens(LINT_DECL_TOKENS, &mut captures) {
535+
self.errors.push(searcher.get_unexpected_err(file));
536+
return;
537+
}
538+
let name_span = captures[0].to_span(file);
539+
let name = searcher.get_capture(captures[0]).to_ascii_lowercase();
540+
if let Some(e) = get_vacant_lint(name, name_span, &mut self.lints, &mut self.errors) {
541+
e.insert(Lint {
542+
name_span,
543+
kind: LintKind::Active(ActiveLint {
544+
decl_span: Span {
545+
file,
546+
start,
547+
end: searcher.pos(),
548+
},
549+
group: searcher.get_capture(captures[1]).into(),
550+
}),
551+
});
552+
}
553+
while searcher.match_tokens(LINT_DECL_EXTRA_TOKENS, &mut []) {
554+
// nothing
555+
}
556+
if !searcher.match_token(CloseBrace) {
557+
self.errors.push(searcher.get_unexpected_err(file));
558+
return;
559+
}
560+
},
561+
"impl_lint_pass" | "declare_lint_pass" => {
562+
if !searcher.match_tokens(LINT_PASS_TOKENS, &mut captures) {
563+
self.errors.push(searcher.get_unexpected_err(file));
564+
return;
565+
}
566+
let pass = self.lint_passes.next_index();
567+
let pass_name = captures[0];
568+
while searcher.match_tokens(&[AnyComment, CaptureIdent], &mut captures) {
569+
self.lint_registrations.push(LintRegistration {
570+
name: searcher.get_capture(captures[0]).to_ascii_lowercase(),
571+
pass,
572+
});
573+
if !searcher.match_token(Comma) {
574+
break;
575+
}
576+
}
577+
if !searcher.match_tokens(&[CloseBracket, CloseParen], &mut []) {
578+
self.errors.push(searcher.get_unexpected_err(file));
579+
return;
580+
}
581+
self.lint_passes.push(LintPassData {
582+
name: searcher.get_capture(pass_name).to_owned(),
583+
mac_span: Span {
481584
file,
482585
start,
483586
end: searcher.pos(),
484587
},
485-
group: searcher.get_capture(captures[1]).into(),
486-
}),
487-
});
488-
}
489-
while searcher.match_tokens(EXTRA_TOKENS, &mut []) {
490-
// nothing
491-
}
492-
if !searcher.match_token(CloseBrace) {
493-
self.errors.push(searcher.get_unexpected_err(file));
494-
return;
588+
});
589+
},
590+
_ => {},
495591
}
496592
}
497593
}
@@ -535,11 +631,11 @@ impl Parser {
535631
let mut searcher = RustSearcher::new(&file_data.contents);
536632
// First instance is the macro definition.
537633
assert!(
538-
searcher.find_token(Ident("declare_with_version")),
539-
"error parsing `clippy_lints/src/deprecated_lints.rs`"
634+
searcher.find_ident("declare_with_version"),
635+
"error parsing `clippy_lints/src/deprecated_lints.rs`",
540636
);
541637

542-
if !searcher.find_token(Ident("declare_with_version")) || !searcher.match_tokens(DEPRECATED_TOKENS, &mut []) {
638+
if !searcher.find_ident("declare_with_version") || !searcher.match_tokens(DEPRECATED_TOKENS, &mut []) {
543639
self.errors.push(searcher.get_unexpected_err(file));
544640
return;
545641
}
@@ -568,7 +664,7 @@ impl Parser {
568664
return;
569665
}
570666

571-
if !searcher.find_token(Ident("declare_with_version")) || !searcher.match_tokens(RENAMED_TOKENS, &mut []) {
667+
if !searcher.find_ident("declare_with_version") || !searcher.match_tokens(RENAMED_TOKENS, &mut []) {
572668
self.errors.push(searcher.get_unexpected_err(file));
573669
return;
574670
}

0 commit comments

Comments
 (0)