From 1011e083cd4b87c71ee042e07345dc547bcfc055 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 4 Nov 2021 12:09:15 +0100 Subject: Reference nightly-rustc docs in clippy's docs --- doc/adding_lints.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index bd32696d6db..ae2444f0171 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -634,7 +634,7 @@ in the following steps: Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here (`implements_trait`, `match_def_path`, `snippet`, etc) + is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] * [The `if_chain` macro][if_chain] * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] @@ -660,7 +660,10 @@ documentation currently. This is unfortunate, but in most cases you can probably get away with copying things from existing similar lints. If you are stuck, don't hesitate to ask on [Zulip] or in the issue/PR. -[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs +[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html +[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html +[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html +[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html [if_chain]: https://docs.rs/if_chain/*/if_chain/ [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html -- cgit 1.4.1-3-g733a5 From d134dddf706e16eb876a3b42c0a7f546626e5954 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 4 Nov 2021 12:09:48 +0100 Subject: Improve `clippy_utils` function docs --- clippy_utils/src/source.rs | 14 +++++++++++--- clippy_utils/src/ty.rs | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 789079510c5..7f68cc388eb 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -155,14 +155,22 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, .join("\n") } -/// Converts a span to a code snippet if available, otherwise use default. +/// Converts a span to a code snippet if available, otherwise returns the default. /// /// This is useful if you want to provide suggestions for your lint or more generally, if you want -/// to convert a given `Span` to a `str`. +/// to convert a given `Span` to a `str`. To create suggestions consider using +/// [`snippet_with_applicability`] to ensure that the applicability stays correct. /// /// # Example /// ```rust,ignore -/// snippet(cx, expr.span, "..") +/// // Given two spans one for `value` and one for the `init` expression. +/// let value = Vec::new(); +/// // ^^^^^ ^^^^^^^^^^ +/// // span1 span2 +/// +/// // The snipped call would return the corresponding code snippet +/// snippet(cx, span1, "..") // -> "value" +/// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index ca64ac7de3e..69715828270 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -114,7 +114,12 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< /// Checks whether a type implements a trait. /// The function returns false in case the type contains an inference variable. -/// See also [`get_trait_def_id`](super::get_trait_def_id). +/// +/// See: +/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`]. +/// * [Common tools for writing lints] for an example how to use this function and other options. +/// +/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait pub fn implements_trait<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, @@ -254,9 +259,17 @@ pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_ite } } -/// Checks if the type is equal to a diagnostic item +/// Checks if the type is equal to a diagnostic item. To check if a type implements a +/// trait marked with a diagnostic item use [`implements_trait`]. +/// +/// For a further exploitation what diagnostic items are see [diagnostic items] in +/// rustc-dev-guide. +/// +/// --- /// /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` +/// +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { match ty.kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), -- cgit 1.4.1-3-g733a5 From a276cd2a000f288b22c25fc13e2eef44f5fb42b4 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 4 Nov 2021 12:39:42 +0100 Subject: Add clippy docs to check for a specific type --- doc/common_tools_writing_lints.md | 54 ++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 1a6b7c8cb47..3390e60297c 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -4,7 +4,8 @@ You may need following tooltips to catch up with common operations. - [Common tools for writing lints](#common-tools-for-writing-lints) - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression) - - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method) + - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method) + - [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) @@ -15,7 +16,7 @@ Useful Rustc dev guide links: - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) -# Retrieving the type of an expression +## Retrieving the type of an expression Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions: @@ -54,7 +55,7 @@ Two noticeable items here: created by type checking step, it includes useful information such as types of expressions, ways to resolve methods and so on. -# Checking if an expr is calling a specific method +## Checking if an expr is calling a specific method Starting with an `expr`, you can check whether it is calling a specific method `some_method`: @@ -63,9 +64,11 @@ impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind; + if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..], _) = &expr.kind; // Check the name of this method is `some_method` if path.ident.name == sym!(some_method); + // Optionally, check the type of the self argument. + // - See "Checking for a specific type" then { // ... } @@ -74,7 +77,44 @@ impl LateLintPass<'_> for MyStructLint { } ``` -# Checking if a type implements a specific trait +## Checking for a specific type + +There are three ways to check if an expression type is a specific type we want to check for. +All of these methods only check for the base type, generic arguments have to be checked separately. + +```rust +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::{paths, match_def_path}; +use rustc_span::symbol::sym; +use rustc_hir::LangItem; + +impl LateLintPass<'_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // Getting the expression type + let ty = cx.typeck_results().expr_ty(expr); + + // 1. Using diagnostic items + // The last argument is the diagnostic item to check for + if is_type_diagnostic_item(cx, ty, sym::Option) { + // The type is an `Option` + } + + // 2. Using lang items + if is_type_lang_item(cx, ty, LangItem::RangeFull) { + // The type is a full range like `.drain(..)` + } + + // 3. Using the type path + if match_def_path(cx, def_id, &paths::RESULT) { + // The type is a `core::result::Result` + } + } +} +``` + +Prefer using diagnostic items and lang items where possible. + +## Checking if a type implements a specific trait There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. @@ -114,7 +154,7 @@ impl LateLintPass<'_> for MyStructLint { We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. A list of defined paths for Clippy can be found in [paths.rs][paths] -# Checking if a type defines a specific method +## Checking if a type defines a specific method To check if our type defines a method called `some_method`: @@ -140,7 +180,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { } ``` -# Dealing with macros +## Dealing with macros There are several helpers in [`clippy_utils`][utils] to deal with macros: -- cgit 1.4.1-3-g733a5 From 540515220d849de13f75df868d0b653e2002e59a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 4 Nov 2021 13:27:08 +0100 Subject: Added note about the usage of `clippy_utils::path` --- doc/common_tools_writing_lints.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 3390e60297c..552b9b2587b 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -105,6 +105,7 @@ impl LateLintPass<'_> for MyStructLint { } // 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` } @@ -142,6 +143,7 @@ impl LateLintPass<'_> for MyStructLint { // 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 } -- cgit 1.4.1-3-g733a5