- [Checking for a specific type](#checking-for-a-specific-type)
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
- [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
- - [Dealing with macros](#dealing-with-macros)
+ - [Dealing with macros](#dealing-with-macros-and-expansions)
Useful Rustc dev guide links:
- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
}
// 3. Using the type path
+ // This method should be avoided if possible
if match_def_path(cx, def_id, &paths::RESULT) {
// The type is a `core::result::Result`
}
// 3. Using the type path with the expression
// we use `match_trait_method` function from Clippy's utils
+ // (This method should be avoided if possible)
if match_trait_method(cx, expr, &paths::INTO) {
// `expr` implements `Into` trait
}
}
```
-## Dealing with macros
+## Dealing with macros and expansions
-There are several helpers in [`clippy_utils`][utils] to deal with macros:
+Keep in mind that macros are already expanded and desugaring is already applied
+to the code representation that you are working with in Clippy. This unfortunately causes a lot of
+false positives because macro expansions are "invisible" unless you actively check for them.
+Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be
+dynamic in ways that are difficult or impossible to see.
+Use the following functions to deal with macros:
-- `in_macro()`: detect if the given span is expanded by a macro
+- `span.from_expansion()`: detects if a span is from macro expansion or desugaring.
+ Checking this is a common first step in a lint.
-You may want to use this for example to not start linting in any macro.
+ ```rust
+ if expr.span.from_expansion() {
+ // just forget it
+ return;
+ }
+ ```
-```rust
-macro_rules! foo {
- ($param:expr) => {
- match $param {
- "bar" => println!("whatever"),
- _ => ()
- }
- };
-}
+- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it.
+ It is sometimes useful to check if the context of two spans are equal.
-foo!("bar");
+ ```rust
+ // expands to `1 + 0`, but don't lint
+ 1 + mac!()
+ ```
+ ```rust
+ if left.span.ctxt() != right.span.ctxt() {
+ // the coder most likely cannot modify this expression
+ return;
+ }
+ ```
+ Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can
+ be assumed to have the same context. And so just using `span.from_expansion()` is often good enough.
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_macro(match_span), true);
-```
-- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
+- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate.
+ If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros
+ not defined in the current crate. It doesn't make sense to lint code that the coder can't change.
-You may want to use it for example to not start linting in macros from other crates
+ You may want to use it for example to not start linting in macros from other crates
-```rust
-#[macro_use]
-extern crate a_crate_with_macros;
+ ```rust
+ #[macro_use]
+ extern crate a_crate_with_macros;
-// `foo` is defined in `a_crate_with_macros`
-foo!("bar");
+ // `foo` is defined in `a_crate_with_macros`
+ foo!("bar");
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_external_macro(cx.sess(), match_span), true);
-```
+ // if we lint the `match` of `foo` call and test its span
+ assert_eq!(in_external_macro(cx.sess(), match_span), true);
+ ```
- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
-```rust
-macro_rules! m {
- ($a:expr, $b:expr) => {
- if $a.is_some() {
- $b;
- }
- }
-}
+ ```rust
+ macro_rules! m {
+ ($a:expr, $b:expr) => {
+ if $a.is_some() {
+ $b;
+ }
+ }
+ }
-let x: Option<u32> = Some(42);
-m!(x, x.unwrap());
+ let x: Option<u32> = Some(42);
+ m!(x, x.unwrap());
-// These spans are not from the same context
-// x.is_some() is from inside the macro
-// x.unwrap() is from outside the macro
-assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
-```
+ // These spans are not from the same context
+ // x.is_some() is from inside the macro
+ // x.unwrap() is from outside the macro
+ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
+ ```
[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
[paths]: ../clippy_utils/src/paths.rs
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs