@@ -2,42 +2,109 @@ pub mod cursor;
22
33use self :: cursor:: { Capture , Cursor } ;
44use crate :: utils:: { ErrAction , File , Scoped , expect_action, walk_dir_no_dot_or_target} ;
5+ use core:: fmt:: { Display , Write as _} ;
56use core:: range:: Range ;
7+ use rustc_arena:: DroplessArena ;
68use std:: fs;
79use std:: path:: { self , Path , PathBuf } ;
10+ use std:: str:: pattern:: Pattern ;
811
9- pub struct ParseCxImpl ;
10- pub type ParseCx < ' cx > = & ' cx mut ParseCxImpl ;
12+ pub struct ParseCxImpl < ' cx > {
13+ pub arena : & ' cx DroplessArena ,
14+ pub str_buf : StrBuf ,
15+ }
16+ pub type ParseCx < ' cx > = & ' cx mut ParseCxImpl < ' cx > ;
1117
1218/// Calls the given function inside a newly created parsing context.
13- pub fn new_parse_cx < ' env , T > ( f : impl for < ' cx > FnOnce ( & ' cx mut Scoped < ' cx , ' env , ParseCxImpl > ) -> T ) -> T {
14- f ( & mut Scoped :: new ( ParseCxImpl ) )
19+ pub fn new_parse_cx < ' env , T > ( f : impl for < ' cx > FnOnce ( & ' cx mut Scoped < ' cx , ' env , ParseCxImpl < ' cx > > ) -> T ) -> T {
20+ let arena = DroplessArena :: default ( ) ;
21+ f ( & mut Scoped :: new ( ParseCxImpl {
22+ arena : & arena,
23+ str_buf : StrBuf :: with_capacity ( 128 ) ,
24+ } ) )
1525}
1626
17- pub struct Lint {
18- pub name : String ,
19- pub group : String ,
20- pub module : String ,
27+ /// A string used as a temporary buffer used to avoid allocating for short lived strings.
28+ pub struct StrBuf ( String ) ;
29+ impl StrBuf {
30+ /// Creates a new buffer with the specified initial capacity.
31+ pub fn with_capacity ( cap : usize ) -> Self {
32+ Self ( String :: with_capacity ( cap) )
33+ }
34+
35+ /// Allocates the result of formatting the given value onto the arena.
36+ pub fn alloc_display < ' cx > ( & mut self , arena : & ' cx DroplessArena , value : impl Display ) -> & ' cx str {
37+ self . 0 . clear ( ) ;
38+ write ! ( self . 0 , "{value}" ) . expect ( "`Display` impl returned an error" ) ;
39+ arena. alloc_str ( & self . 0 )
40+ }
41+
42+ /// Allocates the string onto the arena with all ascii characters converted to
43+ /// lowercase.
44+ pub fn alloc_ascii_lower < ' cx > ( & mut self , arena : & ' cx DroplessArena , s : & str ) -> & ' cx str {
45+ self . 0 . clear ( ) ;
46+ self . 0 . push_str ( s) ;
47+ self . 0 . make_ascii_lowercase ( ) ;
48+ arena. alloc_str ( & self . 0 )
49+ }
50+
51+ /// Allocates the result of replacing all instances the pattern with the given string
52+ /// onto the arena.
53+ pub fn alloc_replaced < ' cx > (
54+ & mut self ,
55+ arena : & ' cx DroplessArena ,
56+ s : & str ,
57+ pat : impl Pattern ,
58+ replacement : & str ,
59+ ) -> & ' cx str {
60+ let mut parts = s. split ( pat) ;
61+ let Some ( first) = parts. next ( ) else {
62+ return "" ;
63+ } ;
64+ self . 0 . clear ( ) ;
65+ self . 0 . push_str ( first) ;
66+ for part in parts {
67+ self . 0 . push_str ( replacement) ;
68+ self . 0 . push_str ( part) ;
69+ }
70+ if self . 0 . is_empty ( ) {
71+ ""
72+ } else {
73+ arena. alloc_str ( & self . 0 )
74+ }
75+ }
76+
77+ /// Performs an operation with the freshly cleared buffer.
78+ pub fn with < T > ( & mut self , f : impl FnOnce ( & mut String ) -> T ) -> T {
79+ self . 0 . clear ( ) ;
80+ f ( & mut self . 0 )
81+ }
82+ }
83+
84+ pub struct Lint < ' cx > {
85+ pub name : & ' cx str ,
86+ pub group : & ' cx str ,
87+ pub module : & ' cx str ,
2188 pub path : PathBuf ,
2289 pub declaration_range : Range < usize > ,
2390}
2491
25- pub struct DeprecatedLint {
26- pub name : String ,
27- pub reason : String ,
28- pub version : String ,
92+ pub struct DeprecatedLint < ' cx > {
93+ pub name : & ' cx str ,
94+ pub reason : & ' cx str ,
95+ pub version : & ' cx str ,
2996}
3097
31- pub struct RenamedLint {
32- pub old_name : String ,
33- pub new_name : String ,
34- pub version : String ,
98+ pub struct RenamedLint < ' cx > {
99+ pub old_name : & ' cx str ,
100+ pub new_name : & ' cx str ,
101+ pub version : & ' cx str ,
35102}
36103
37- impl ParseCxImpl {
104+ impl < ' cx > ParseCxImpl < ' cx > {
38105 /// Finds all lint declarations (`declare_clippy_lint!`)
39106 #[ must_use]
40- pub fn find_lint_decls ( & mut self ) -> Vec < Lint > {
107+ pub fn find_lint_decls ( & mut self ) -> Vec < Lint < ' cx > > {
41108 let mut lints = Vec :: with_capacity ( 1000 ) ;
42109 let mut contents = String :: new ( ) ;
43110 for e in expect_action ( fs:: read_dir ( "." ) , ErrAction :: Read , "." ) {
@@ -63,29 +130,59 @@ impl ParseCxImpl {
63130 && let Some ( path) = path. get ( crate_path. len ( ) + 1 ..)
64131 {
65132 let module = if path == "lib" {
66- String :: new ( )
133+ ""
67134 } else {
68135 let path = path
69136 . strip_suffix ( "mod" )
70137 . and_then ( |x| x. strip_suffix ( path:: MAIN_SEPARATOR ) )
71138 . unwrap_or ( path) ;
72- path. replace ( [ '/' , '\\' ] , "::" )
139+ self . str_buf
140+ . alloc_replaced ( self . arena , path, path:: MAIN_SEPARATOR , "::" )
73141 } ;
74- parse_clippy_lint_decls (
142+ self . parse_clippy_lint_decls (
75143 e. path ( ) ,
76144 File :: open_read_to_cleared_string ( e. path ( ) , & mut contents) ,
77- & module,
145+ module,
78146 & mut lints,
79147 ) ;
80148 }
81149 }
82150 }
83- lints. sort_by ( |lhs, rhs| lhs. name . cmp ( & rhs. name ) ) ;
151+ lints. sort_by ( |lhs, rhs| lhs. name . cmp ( rhs. name ) ) ;
84152 lints
85153 }
86154
155+ /// Parse a source file looking for `declare_clippy_lint` macro invocations.
156+ fn parse_clippy_lint_decls ( & mut self , path : & Path , contents : & str , module : & ' cx str , lints : & mut Vec < Lint < ' cx > > ) {
157+ #[ allow( clippy:: enum_glob_use) ]
158+ use cursor:: Pat :: * ;
159+ #[ rustfmt:: skip]
160+ static DECL_TOKENS : & [ cursor:: Pat < ' _ > ] = & [
161+ // !{ /// docs
162+ Bang , OpenBrace , AnyComment ,
163+ // #[clippy::version = "version"]
164+ Pound , OpenBracket , Ident ( "clippy" ) , DoubleColon , Ident ( "version" ) , Eq , LitStr , CloseBracket ,
165+ // pub NAME, GROUP,
166+ Ident ( "pub" ) , CaptureIdent , Comma , AnyComment , CaptureIdent , Comma ,
167+ ] ;
168+
169+ let mut cursor = Cursor :: new ( contents) ;
170+ let mut captures = [ Capture :: EMPTY ; 2 ] ;
171+ while let Some ( start) = cursor. find_ident ( "declare_clippy_lint" ) {
172+ if cursor. match_all ( DECL_TOKENS , & mut captures) && cursor. find_pat ( CloseBrace ) {
173+ lints. push ( Lint {
174+ name : self . str_buf . alloc_ascii_lower ( self . arena , cursor. get_text ( captures[ 0 ] ) ) ,
175+ group : self . arena . alloc_str ( cursor. get_text ( captures[ 1 ] ) ) ,
176+ module,
177+ path : path. into ( ) ,
178+ declaration_range : start as usize ..cursor. pos ( ) as usize ,
179+ } ) ;
180+ }
181+ }
182+ }
183+
87184 #[ must_use]
88- pub fn read_deprecated_lints ( & mut self ) -> ( Vec < DeprecatedLint > , Vec < RenamedLint > ) {
185+ pub fn read_deprecated_lints ( & mut self ) -> ( Vec < DeprecatedLint < ' cx > > , Vec < RenamedLint < ' cx > > ) {
89186 #[ allow( clippy:: enum_glob_use) ]
90187 use cursor:: Pat :: * ;
91188 #[ rustfmt:: skip]
@@ -124,9 +221,9 @@ impl ParseCxImpl {
124221 if cursor. find_ident ( "declare_with_version" ) . is_some ( ) && cursor. match_all ( DEPRECATED_TOKENS , & mut [ ] ) {
125222 while cursor. match_all ( DECL_TOKENS , & mut captures) {
126223 deprecated. push ( DeprecatedLint {
127- name : parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 1 ] ) ) ,
128- reason : parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 2 ] ) ) ,
129- version : parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 0 ] ) ) ,
224+ name : self . parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 1 ] ) ) ,
225+ reason : self . parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 2 ] ) ) ,
226+ version : self . parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 0 ] ) ) ,
130227 } ) ;
131228 }
132229 } else {
@@ -136,81 +233,53 @@ impl ParseCxImpl {
136233 if cursor. find_ident ( "declare_with_version" ) . is_some ( ) && cursor. match_all ( RENAMED_TOKENS , & mut [ ] ) {
137234 while cursor. match_all ( DECL_TOKENS , & mut captures) {
138235 renamed. push ( RenamedLint {
139- old_name : parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 1 ] ) ) ,
140- new_name : parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 2 ] ) ) ,
141- version : parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 0 ] ) ) ,
236+ old_name : self . parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 1 ] ) ) ,
237+ new_name : self . parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 2 ] ) ) ,
238+ version : self . parse_str_single_line ( path. as_ref ( ) , cursor. get_text ( captures[ 0 ] ) ) ,
142239 } ) ;
143240 }
144241 } else {
145242 panic ! ( "error reading renamed lints" ) ;
146243 }
147244
148- deprecated. sort_by ( |lhs, rhs| lhs. name . cmp ( & rhs. name ) ) ;
149- renamed. sort_by ( |lhs, rhs| lhs. old_name . cmp ( & rhs. old_name ) ) ;
245+ deprecated. sort_by ( |lhs, rhs| lhs. name . cmp ( rhs. name ) ) ;
246+ renamed. sort_by ( |lhs, rhs| lhs. old_name . cmp ( rhs. old_name ) ) ;
150247 ( deprecated, renamed)
151248 }
152- }
153249
154- /// Parse a source file looking for `declare_clippy_lint` macro invocations.
155- fn parse_clippy_lint_decls ( path : & Path , contents : & str , module : & str , lints : & mut Vec < Lint > ) {
156- #[ allow( clippy:: enum_glob_use) ]
157- use cursor:: Pat :: * ;
158- #[ rustfmt:: skip]
159- static DECL_TOKENS : & [ cursor:: Pat < ' _ > ] = & [
160- // !{ /// docs
161- Bang , OpenBrace , AnyComment ,
162- // #[clippy::version = "version"]
163- Pound , OpenBracket , Ident ( "clippy" ) , DoubleColon , Ident ( "version" ) , Eq , LitStr , CloseBracket ,
164- // pub NAME, GROUP,
165- Ident ( "pub" ) , CaptureIdent , Comma , AnyComment , CaptureIdent , Comma ,
166- ] ;
167-
168- let mut cursor = Cursor :: new ( contents) ;
169- let mut captures = [ Capture :: EMPTY ; 2 ] ;
170- while let Some ( start) = cursor. find_ident ( "declare_clippy_lint" ) {
171- if cursor. match_all ( DECL_TOKENS , & mut captures) && cursor. find_pat ( CloseBrace ) {
172- lints. push ( Lint {
173- name : cursor. get_text ( captures[ 0 ] ) . to_lowercase ( ) ,
174- group : cursor. get_text ( captures[ 1 ] ) . into ( ) ,
175- module : module. into ( ) ,
176- path : path. into ( ) ,
177- declaration_range : start as usize ..cursor. pos ( ) as usize ,
178- } ) ;
250+ /// Removes the line splices and surrounding quotes from a string literal
251+ fn parse_str_lit ( & mut self , s : & str ) -> & ' cx str {
252+ let ( s, is_raw) = if let Some ( s) = s. strip_prefix ( "r" ) {
253+ ( s. trim_matches ( '#' ) , true )
254+ } else {
255+ ( s, false )
256+ } ;
257+ let s = s
258+ . strip_prefix ( '"' )
259+ . and_then ( |s| s. strip_suffix ( '"' ) )
260+ . unwrap_or_else ( || panic ! ( "expected quoted string, found `{s}`" ) ) ;
261+
262+ if is_raw {
263+ if s. is_empty ( ) { "" } else { self . arena . alloc_str ( s) }
264+ } else {
265+ self . str_buf . with ( |buf| {
266+ rustc_literal_escaper:: unescape_str ( s, & mut |_, ch| {
267+ if let Ok ( ch) = ch {
268+ buf. push ( ch) ;
269+ }
270+ } ) ;
271+ if buf. is_empty ( ) { "" } else { self . arena . alloc_str ( buf) }
272+ } )
179273 }
180274 }
181- }
182275
183- /// Removes the line splices and surrounding quotes from a string literal
184- fn parse_str_lit ( s : & str ) -> String {
185- let ( s, is_raw) = if let Some ( s) = s. strip_prefix ( "r" ) {
186- ( s. trim_matches ( '#' ) , true )
187- } else {
188- ( s, false )
189- } ;
190- let s = s
191- . strip_prefix ( '"' )
192- . and_then ( |s| s. strip_suffix ( '"' ) )
193- . unwrap_or_else ( || panic ! ( "expected quoted string, found `{s}`" ) ) ;
194-
195- if is_raw {
196- s. into ( )
197- } else {
198- let mut res = String :: with_capacity ( s. len ( ) ) ;
199- rustc_literal_escaper:: unescape_str ( s, & mut |_, ch| {
200- if let Ok ( ch) = ch {
201- res. push ( ch) ;
202- }
203- } ) ;
204- res
276+ fn parse_str_single_line ( & mut self , path : & Path , s : & str ) -> & ' cx str {
277+ let value = self . parse_str_lit ( s) ;
278+ assert ! (
279+ !value. contains( '\n' ) ,
280+ "error parsing `{}`: `{s}` should be a single line string" ,
281+ path. display( ) ,
282+ ) ;
283+ value
205284 }
206285}
207-
208- fn parse_str_single_line ( path : & Path , s : & str ) -> String {
209- let value = parse_str_lit ( s) ;
210- assert ! (
211- !value. contains( '\n' ) ,
212- "error parsing `{}`: `{s}` should be a single line string" ,
213- path. display( ) ,
214- ) ;
215- value
216- }
0 commit comments