@@ -8,7 +8,7 @@ You may need following tooltips to catch up with common operations.
88 - [ Checking for a specific type] ( #checking-for-a-specific-type )
99 - [ Checking if a type implements a specific trait] ( #checking-if-a-type-implements-a-specific-trait )
1010 - [ 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 )
1212
1313Useful Rustc dev guide links:
1414- [ 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 {
182182}
183183```
184184
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+ ```
243255
244256[ TyS ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
245257[ TyKind ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
@@ -249,4 +261,3 @@ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
249261[ TyCtxt ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
250262[ pat_ty ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
251263[ paths ] : ../clippy_utils/src/paths.rs
252- [ utils ] : https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
0 commit comments