@@ -8,7 +8,7 @@ You may need following tooltips to catch up with common operations.
8
8
- [ Checking for a specific type] ( #checking-for-a-specific-type )
9
9
- [ Checking if a type implements a specific trait] ( #checking-if-a-type-implements-a-specific-trait )
10
10
- [ Checking if a type defines a specific method] ( #checking-if-a-type-defines-a-specific-method )
11
- - [ Dealing with macros] ( #dealing-with-macros )
11
+ - [ Dealing with macros] ( #dealing-with-macros-and-expansions )
12
12
13
13
Useful Rustc dev guide links:
14
14
- [ Stages of compilation] ( https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation )
@@ -182,64 +182,76 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
182
182
}
183
183
```
184
184
185
- ## Dealing with macros
186
-
187
- There are several helpers in [ ` clippy_utils ` ] [ utils ] to deal with macros:
188
-
189
- - ` in_macro() ` : detect if the given span is expanded by a macro
190
-
191
- You may want to use this for example to not start linting in any macro.
192
-
193
- ``` rust
194
- macro_rules! foo {
195
- ($ param : expr ) => {
196
- match $ param {
197
- " bar" => println! (" whatever" ),
198
- _ => ()
199
- }
200
- };
201
- }
202
-
203
- foo! (" bar" );
204
-
205
- // if we lint the `match` of `foo` call and test its span
206
- assert_eq! (in_macro (match_span ), true );
207
- ```
208
-
209
- - ` in_external_macro() ` : detect if the given span is from an external macro, defined in a foreign crate
210
-
211
- You may want to use it for example to not start linting in macros from other crates
212
-
213
- ``` rust
214
- #[macro_use]
215
- extern crate a_crate_with_macros;
216
-
217
- // `foo` is defined in `a_crate_with_macros`
218
- foo! (" bar" );
219
-
220
- // if we lint the `match` of `foo` call and test its span
221
- assert_eq! (in_external_macro (cx . sess (), match_span ), true );
222
- ```
223
-
224
- - ` differing_macro_contexts() ` : returns true if the two given spans are not from the same context
225
-
226
- ``` rust
227
- macro_rules! m {
228
- ($ a : expr , $ b : expr ) => {
229
- if $ a . is_some () {
230
- $ b ;
231
- }
232
- }
233
- }
234
-
235
- let x : Option <u32 > = Some (42 );
236
- m! (x , x . unwrap ());
237
-
238
- // These spans are not from the same context
239
- // x.is_some() is from inside the macro
240
- // x.unwrap() is from outside the macro
241
- assert_eq! (differing_macro_contexts (x_is_some_span , x_unwrap_span ), true );
242
- ```
185
+ ## Dealing with macros and expansions
186
+
187
+ Keep in mind that macros are already expanded and desugaring is already applied
188
+ to the code representation that you are working with in Clippy. This unfortunately causes a lot of
189
+ false positives because macro expansions are "invisible" unless you actively check for them.
190
+ Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be
191
+ dynamic in ways that are difficult or impossible to see.
192
+ Use the following functions to deal with macros:
193
+
194
+ - ` span.from_expansion() ` : detects if a span is from macro expansion or desugaring.
195
+ Checking this is a common first step in a lint.
196
+
197
+ ``` rust
198
+ if expr . span. from_expansion () {
199
+ // just forget it
200
+ return ;
201
+ }
202
+ ```
203
+
204
+ - ` span.ctxt() ` : the span's context represents whether it is from expansion, and if so, which macro call expanded it.
205
+ It is sometimes useful to check if the context of two spans are equal.
206
+
207
+ ``` rust
208
+ // expands to `1 + 0`, but don't lint
209
+ 1 + mac! ()
210
+ ```
211
+ ``` rust
212
+ if left . span. ctxt () != right . span. ctxt () {
213
+ // the coder most likely cannot modify this expression
214
+ return ;
215
+ }
216
+ ```
217
+ Note: Code that is not from expansion is in the "root" context. So any spans where ` from_expansion ` returns ` true ` can
218
+ be assumed to have the same context. And so just using ` span.from_expansion() ` is often good enough.
219
+
220
+
221
+ - ` in_external_macro(span) ` : detect if the given span is from a macro defined in a foreign crate.
222
+ If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros
223
+ not defined in the current crate. It doesn't make sense to lint code that the coder can't change.
224
+
225
+ You may want to use it for example to not start linting in macros from other crates
226
+
227
+ ``` rust
228
+ #[macro_use]
229
+ extern crate a_crate_with_macros;
230
+
231
+ // `foo` is defined in `a_crate_with_macros`
232
+ foo! (" bar" );
233
+
234
+ // if we lint the `match` of `foo` call and test its span
235
+ assert_eq! (in_external_macro (cx . sess (), match_span ), true );
236
+ ```
237
+
238
+ ``` rust
239
+ macro_rules! m {
240
+ ($ a : expr , $ b : expr ) => {
241
+ if $ a . is_some () {
242
+ $ b ;
243
+ }
244
+ }
245
+ }
246
+
247
+ let x : Option <u32 > = Some (42 );
248
+ m! (x , x . unwrap ());
249
+
250
+ // These spans are not from the same context
251
+ // x.is_some() is from inside the macro
252
+ // x.unwrap() is from outside the macro
253
+ assert_eq! (differing_macro_contexts (x_is_some_span , x_unwrap_span ), true );
254
+ ```
243
255
244
256
[ TyS ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
245
257
[ TyKind ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
0 commit comments