mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Merge commit 'f0cdee4a3f094416189261481eae374b76792af1' into clippy-subtree-sync
This commit is contained in:
parent
3a760909fa
commit
c9a43b18f1
417 changed files with 3662 additions and 1176 deletions
4
.github/workflows/deploy.yml
vendored
4
.github/workflows/deploy.yml
vendored
|
@ -52,7 +52,9 @@ jobs:
|
|||
run: cargo generate-lockfile
|
||||
|
||||
- name: Cache
|
||||
uses: Swatinem/rust-cache@v1.3.0
|
||||
uses: Swatinem/rust-cache@v2.7.0
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: cargo collect-metadata
|
||||
run: cargo collect-metadata
|
||||
|
|
|
@ -5128,6 +5128,7 @@ Released 2018-09-13
|
|||
[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
|
||||
[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
|
||||
[`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns
|
||||
[`impl_hash_borrow_with_str_and_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_hash_borrow_with_str_and_bytes
|
||||
[`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params
|
||||
[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
|
||||
[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
|
||||
|
@ -5189,6 +5190,7 @@ Released 2018-09-13
|
|||
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
|
||||
[`iter_without_into_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_without_into_iter
|
||||
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
|
||||
[`join_absolute_paths`]: https://rust-lang.github.io/rust-clippy/master/index.html#join_absolute_paths
|
||||
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
|
||||
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
|
||||
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
|
||||
|
@ -5380,6 +5382,7 @@ Released 2018-09-13
|
|||
[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
|
||||
[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
|
||||
[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
|
||||
[`option_map_or_err_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_err_ok
|
||||
[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
|
||||
[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
|
||||
[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
|
||||
|
@ -5542,6 +5545,7 @@ Released 2018-09-13
|
|||
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
|
||||
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
|
||||
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
|
||||
[`test_attr_in_doctest`]: https://rust-lang.github.io/rust-clippy/master/index.html#test_attr_in_doctest
|
||||
[`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module
|
||||
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
|
||||
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
|
||||
|
@ -5738,4 +5742,5 @@ Released 2018-09-13
|
|||
[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
|
||||
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
|
||||
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
|
||||
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
|
||||
<!-- end autogenerated links to configuration documentation -->
|
||||
|
|
|
@ -202,7 +202,7 @@ is. This file has already imported some initial things we will need:
|
|||
|
||||
```rust
|
||||
use rustc_lint::{EarlyLintPass, EarlyContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_ast::ast::*;
|
||||
```
|
||||
|
||||
|
@ -518,6 +518,8 @@ define_Conf! {
|
|||
|
||||
[`clippy_config::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_config/msrvs/index.html
|
||||
|
||||
Afterwards update the documentation for the book as described in [Adding configuration to a lint](#adding-configuration-to-a-lint).
|
||||
|
||||
## Author lint
|
||||
|
||||
If you have trouble implementing your lint, there is also the internal `author`
|
||||
|
|
|
@ -119,7 +119,7 @@ an `u32`. As far as `hir::Ty` is concerned those might be different types. But a
|
|||
understands that they're the same type, in-depth lifetimes, etc...
|
||||
|
||||
To get from a `hir::Ty` to a `ty::Ty`, you can use the [`hir_ty_to_ty`][hir_ty_to_ty] function outside of bodies or
|
||||
outside of bodies the [`TypeckResults::node_type()`][node_type] method.
|
||||
the [`TypeckResults::node_type()`][node_type] method inside of bodies.
|
||||
|
||||
> **Warning**: Don't use `hir_ty_to_ty` inside of bodies, because this can cause ICEs.
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions)
|
||||
* [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold)
|
||||
* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one)
|
||||
* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
|
||||
|
||||
|
||||
## `cognitive-complexity-threshold`
|
||||
|
@ -791,3 +792,16 @@ for _ in &mut *rmvec {}
|
|||
* [`explicit_iter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop)
|
||||
|
||||
|
||||
## `check-private-items`
|
||||
|
||||
|
||||
**Default Value:** `false`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`missing_safety_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc)
|
||||
* [`unnecessary_safety_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc)
|
||||
* [`missing_panics_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc)
|
||||
* [`missing_errors_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc)
|
||||
|
||||
|
||||
|
|
|
@ -249,7 +249,7 @@ define_Conf! {
|
|||
///
|
||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
(avoid_breaking_exported_api: bool = true),
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE.
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP.
|
||||
///
|
||||
/// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
|
||||
#[default_text = ""]
|
||||
|
@ -543,6 +543,10 @@ define_Conf! {
|
|||
/// for _ in &mut *rmvec {}
|
||||
/// ```
|
||||
(enforce_iter_loop_reborrow: bool = false),
|
||||
/// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC
|
||||
///
|
||||
/// Whether to also run the listed lints on private items.
|
||||
(check_private_items: bool = false),
|
||||
}
|
||||
|
||||
/// Search for the configuration file.
|
||||
|
|
|
@ -23,6 +23,7 @@ msrv_aliases! {
|
|||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
||||
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
|
||||
1,55,0 { SEEK_REWIND }
|
||||
1,54,0 { INTO_KEYS }
|
||||
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
|
||||
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
|
||||
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
|
||||
|
|
|
@ -283,7 +283,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
use clippy_utils::msrvs::{{self, Msrv}};
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
||||
use rustc_session::{{declare_tool_lint, impl_lint_pass}};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
"#
|
||||
)
|
||||
|
@ -292,7 +292,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
r#"
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}}};
|
||||
use rustc_session::{{declare_lint_pass, declare_tool_lint}};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
"#
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hir::def::{DefKind, Res};
|
|||
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
|
||||
use rustc_hir::{HirId, ItemKind, Node, Path};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_ast as ast;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimit
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -4,7 +4,7 @@ use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
|
|||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol;
|
||||
use std::f64::consts as f64;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::is_from_proc_macro;
|
|||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum AsmStyle {
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
|||
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_hir::def::Res;
|
|||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::ty::implements_trait;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, BodyId, CoroutineKind, CoroutineSource, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -17,7 +17,7 @@ use rustc_hir::{
|
|||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::{declare_lint_pass, impl_lint_pass};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, Span, DUMMY_SP};
|
||||
use semver::Version;
|
||||
|
@ -120,7 +120,8 @@ declare_clippy_lint! {
|
|||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[deprecated]` annotations with a `since`
|
||||
/// field that is not a valid semantic version.
|
||||
/// field that is not a valid semantic version. Also allows "TBD" to signal
|
||||
/// future deprecation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For checking the version of the deprecation, it must be
|
||||
|
@ -405,20 +406,26 @@ declare_clippy_lint! {
|
|||
/// Checks for `#[cfg(features = "...")]` and suggests to replace it with
|
||||
/// `#[cfg(feature = "...")]`.
|
||||
///
|
||||
/// It also checks if `cfg(test)` was misspelled.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Misspelling `feature` as `features` can be sometimes hard to spot. It
|
||||
/// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It
|
||||
/// may cause conditional compilation not work quietly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(features = "some-feature")]
|
||||
/// fn conditional() { }
|
||||
/// #[cfg(tests)]
|
||||
/// mod tests { }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(feature = "some-feature")]
|
||||
/// fn conditional() { }
|
||||
/// #[cfg(test)]
|
||||
/// mod tests { }
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub MAYBE_MISUSED_CFG,
|
||||
|
@ -473,7 +480,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
&& let MetaItemKind::NameValue(lit) = &mi.kind
|
||||
&& mi.has_name(sym::since)
|
||||
{
|
||||
check_semver(cx, item.span(), lit);
|
||||
check_deprecated_since(cx, item.span(), lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -754,9 +761,9 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut
|
|||
}
|
||||
}
|
||||
|
||||
fn check_semver(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) {
|
||||
fn check_deprecated_since(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) {
|
||||
if let LitKind::Str(is, _) = lit.kind {
|
||||
if Version::parse(is.as_str()).is_ok() {
|
||||
if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -923,21 +930,35 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
|||
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||
for item in items {
|
||||
if let NestedMetaItem::MetaItem(meta) = item {
|
||||
if meta.has_name(sym!(features))
|
||||
if let Some(ident) = meta.ident()
|
||||
&& ident.name.as_str() == "features"
|
||||
&& let Some(val) = meta.value_str()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAYBE_MISUSED_CFG,
|
||||
meta.span,
|
||||
"feature may misspelled as features",
|
||||
"use",
|
||||
"'feature' may be misspelled as 'features'",
|
||||
"did you mean",
|
||||
format!("feature = \"{val}\""),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let MetaItemKind::List(list) = &meta.kind {
|
||||
check_nested_misused_cfg(cx, list);
|
||||
// If this is not a list, then we check for `cfg(test)`.
|
||||
} else if let Some(ident) = meta.ident()
|
||||
&& matches!(ident.name.as_str(), "tests" | "Test")
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAYBE_MISUSED_CFG,
|
||||
meta.span,
|
||||
&format!("'test' may be misspelled as '{}'", ident.name.as_str()),
|
||||
"did you mean",
|
||||
"test".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_hir::{Body, CoroutineKind, CoroutineSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::CoroutineLayout;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{BlockCheckMode, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{Expr, ExprKind, Lit};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::higher::If;
|
|||
use rustc_ast::LitKind;
|
||||
use rustc_hir::{Block, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass, Level};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_hir::{ExprKind, UnOp};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
|||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::IsSuggestable;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -45,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault {
|
|||
&& let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind
|
||||
&& let ExprKind::Call(arg_path, ..) = arg.kind
|
||||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
&& (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg))
|
||||
&& (expr.span.eq_ctxt(arg.span) || is_local_vec_expn(cx, arg, expr))
|
||||
&& seg.ident.name == sym::new
|
||||
&& path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box())
|
||||
&& is_default_equivalent(cx, arg)
|
||||
|
@ -81,10 +81,10 @@ fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
macro_backtrace(expr.span)
|
||||
.next()
|
||||
.map_or(false, |call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id))
|
||||
fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) -> bool {
|
||||
macro_backtrace(expr.span).next().map_or(false, |call| {
|
||||
cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -8,7 +8,7 @@ use clippy_utils::diagnostics::span_lint;
|
|||
use clippy_utils::is_lint_allowed;
|
||||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -27,7 +27,7 @@ use clippy_utils::is_hir_ty_cfg_dependant;
|
|||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_ast::ast::Attribute;
|
|||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, Expr, ExprKind, FnDecl};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ use clippy_utils::sugg::Sugg;
|
|||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
|
|||
use core::ops::ControlFlow;
|
||||
use rustc_hir::{Block, ExprKind, HirId, LangItem, Local, Node, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::ty::implements_trait;
|
|||
use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_hir::def_id::DefIdSet;
|
|||
use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::query::Key;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::hygiene::walk_chain;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note;
|
|||
use clippy_utils::ty::is_copy;
|
||||
use rustc_hir::{Impl, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -4,7 +4,7 @@ use rustc_ast::token::{Token, TokenKind};
|
|||
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::{is_in_cfg_test, is_in_test_function};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -140,6 +140,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::doc::MISSING_SAFETY_DOC_INFO,
|
||||
crate::doc::NEEDLESS_DOCTEST_MAIN_INFO,
|
||||
crate::doc::SUSPICIOUS_DOC_COMMENTS_INFO,
|
||||
crate::doc::TEST_ATTR_IN_DOCTEST_INFO,
|
||||
crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
|
||||
crate::double_parens::DOUBLE_PARENS_INFO,
|
||||
crate::drop_forget_ref::DROP_NON_DROP_INFO,
|
||||
|
@ -204,6 +205,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::if_not_else::IF_NOT_ELSE_INFO,
|
||||
crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO,
|
||||
crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO,
|
||||
crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO,
|
||||
crate::implicit_hasher::IMPLICIT_HASHER_INFO,
|
||||
crate::implicit_return::IMPLICIT_RETURN_INFO,
|
||||
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
|
||||
|
@ -376,6 +378,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::ITER_SKIP_NEXT_INFO,
|
||||
crate::methods::ITER_SKIP_ZERO_INFO,
|
||||
crate::methods::ITER_WITH_DRAIN_INFO,
|
||||
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
|
||||
crate::methods::MANUAL_FILTER_MAP_INFO,
|
||||
crate::methods::MANUAL_FIND_MAP_INFO,
|
||||
crate::methods::MANUAL_NEXT_BACK_INFO,
|
||||
|
@ -403,6 +406,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::OK_EXPECT_INFO,
|
||||
crate::methods::OPTION_AS_REF_DEREF_INFO,
|
||||
crate::methods::OPTION_FILTER_MAP_INFO,
|
||||
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
|
||||
crate::methods::OPTION_MAP_OR_NONE_INFO,
|
||||
crate::methods::OR_FUN_CALL_INFO,
|
||||
crate::methods::OR_THEN_UNWRAP_INFO,
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::source::snippet_with_context;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_hir::{Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind
|
|||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use std::iter;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -3,7 +3,7 @@ use rustc_hir::{HirId, Item, ItemKind};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, FieldDef, GenericArg, List};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -17,7 +17,7 @@ use rustc_hir::{
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_hir::{
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
|
||||
use rustc_middle::ty::{self, Adt, AdtDef, GenericArgsRef, Ty, TypeckResults};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -14,7 +14,7 @@ use rustc_middle::ty::{
|
|||
self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate, TraitPredicate, Ty,
|
||||
TyCtxt,
|
||||
};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashSet;
|
|||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{ExpnId, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
|
|||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::is_test_module_or_function;
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::{Item, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint;
|
|||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use unicode_script::{Script, UnicodeScript};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hir::def::Res;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
20
clippy_lints/src/doc/link_with_quotes.rs
Normal file
20
clippy_lints/src/doc/link_with_quotes.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::{Fragments, DOC_LINK_WITH_QUOTES};
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, trimmed_text: &str, range: Range<usize>, fragments: Fragments<'_>) {
|
||||
if ((trimmed_text.starts_with('\'') && trimmed_text.ends_with('\''))
|
||||
|| (trimmed_text.starts_with('"') && trimmed_text.ends_with('"')))
|
||||
&& let Some(span) = fragments.span(cx, range)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
span,
|
||||
"possible intra-doc link using quotes instead of backticks",
|
||||
);
|
||||
}
|
||||
}
|
109
clippy_lints/src/doc/markdown.rs
Normal file
109
clippy_lints/src/doc/markdown.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, SuggestionStyle};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{BytePos, Pos, Span};
|
||||
use url::Url;
|
||||
|
||||
use crate::doc::DOC_MARKDOWN;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
|
||||
for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
|
||||
// Trim punctuation as in `some comment (see foo::bar).`
|
||||
// ^^
|
||||
// Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
|
||||
let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
|
||||
|
||||
// Remove leading or trailing single `:` which may be part of a sentence.
|
||||
if word.starts_with(':') && !word.starts_with("::") {
|
||||
word = word.trim_start_matches(':');
|
||||
}
|
||||
if word.ends_with(':') && !word.ends_with("::") {
|
||||
word = word.trim_end_matches(':');
|
||||
}
|
||||
|
||||
if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust for the current word
|
||||
let offset = word.as_ptr() as usize - text.as_ptr() as usize;
|
||||
let span = Span::new(
|
||||
span.lo() + BytePos::from_usize(offset),
|
||||
span.lo() + BytePos::from_usize(offset + word.len()),
|
||||
span.ctxt(),
|
||||
span.parent(),
|
||||
);
|
||||
|
||||
check_word(cx, word, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
|
||||
/// Checks if a string is upper-camel-case, i.e., starts with an uppercase and
|
||||
/// contains at least two uppercase letters (`Clippy` is ok) and one lower-case
|
||||
/// letter (`NASA` is ok).
|
||||
/// Plurals are also excluded (`IDs` is ok).
|
||||
fn is_camel_case(s: &str) -> bool {
|
||||
if s.starts_with(|c: char| c.is_ascii_digit() | c.is_ascii_lowercase()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let s = s.strip_suffix('s').unwrap_or(s);
|
||||
|
||||
s.chars().all(char::is_alphanumeric)
|
||||
&& s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
|
||||
&& s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
|
||||
}
|
||||
|
||||
fn has_underscore(s: &str) -> bool {
|
||||
s != "_" && !s.contains("\\_") && s.contains('_')
|
||||
}
|
||||
|
||||
fn has_hyphen(s: &str) -> bool {
|
||||
s != "-" && s.contains('-')
|
||||
}
|
||||
|
||||
if let Ok(url) = Url::parse(word) {
|
||||
// try to get around the fact that `foo::bar` parses as a valid URL
|
||||
if !url.cannot_be_a_base() {
|
||||
span_lint(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
|
||||
if has_underscore(word) && has_hyphen(word) {
|
||||
return;
|
||||
}
|
||||
|
||||
if has_underscore(word) || word.contains("::") || is_camel_case(word) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"item in documentation is missing backticks",
|
||||
|diag| {
|
||||
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||
diag.span_suggestion_with_style(
|
||||
span,
|
||||
"try",
|
||||
format!("`{snippet}`"),
|
||||
applicability,
|
||||
// always show the suggestion in a separate line, since the
|
||||
// inline presentation adds another pair of backticks
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
86
clippy_lints/src/doc/missing_headers.rs
Normal file
86
clippy_lints/src/doc/missing_headers.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_doc_hidden, return_ty};
|
||||
use rustc_hir::{BodyId, FnSig, OwnerId, Unsafety};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
|
||||
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
owner_id: OwnerId,
|
||||
sig: &FnSig<'_>,
|
||||
headers: DocHeaders,
|
||||
body_id: Option<BodyId>,
|
||||
panic_span: Option<Span>,
|
||||
check_private_items: bool,
|
||||
) {
|
||||
if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) {
|
||||
return; // Private functions do not require doc comments
|
||||
}
|
||||
|
||||
// do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
|
||||
if !check_private_items
|
||||
&& cx
|
||||
.tcx
|
||||
.hir()
|
||||
.parent_iter(owner_id.into())
|
||||
.any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let span = cx.tcx.def_span(owner_id);
|
||||
match (headers.safety, sig.header.unsafety) {
|
||||
(false, Unsafety::Unsafe) => span_lint(
|
||||
cx,
|
||||
MISSING_SAFETY_DOC,
|
||||
span,
|
||||
"unsafe function's docs miss `# Safety` section",
|
||||
),
|
||||
(true, Unsafety::Normal) => span_lint(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
span,
|
||||
"safe function's docs have unnecessary `# Safety` section",
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
if !headers.panics && panic_span.is_some() {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
MISSING_PANICS_DOC,
|
||||
span,
|
||||
"docs for function which may panic missing `# Panics` section",
|
||||
panic_span,
|
||||
"first possible panic found here",
|
||||
);
|
||||
}
|
||||
if !headers.errors {
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, owner_id), sym::Result) {
|
||||
span_lint(
|
||||
cx,
|
||||
MISSING_ERRORS_DOC,
|
||||
span,
|
||||
"docs for function returning `Result` missing `# Errors` section",
|
||||
);
|
||||
} else if let Some(body_id) = body_id
|
||||
&& let Some(future) = cx.tcx.lang_items().future_trait()
|
||||
&& let typeck = cx.tcx.typeck_body(body_id)
|
||||
&& let body = cx.tcx.hir().body(body_id)
|
||||
&& let ret_ty = typeck.expr_ty(body.value)
|
||||
&& implements_trait(cx, ret_ty, future, &[])
|
||||
&& let ty::Coroutine(_, subs, _) = ret_ty.kind()
|
||||
&& is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MISSING_ERRORS_DOC,
|
||||
span,
|
||||
"docs for function returning `Result` missing `# Errors` section",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,16 @@
|
|||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::Visitable;
|
||||
use clippy_utils::{is_entrypoint_fn, method_chain_args};
|
||||
use pulldown_cmark::Event::{
|
||||
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
|
||||
};
|
||||
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
|
||||
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
|
||||
use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind};
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{AttrKind, AttrStyle};
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::emitter::EmitterWriter;
|
||||
use rustc_errors::{Applicability, Handler, SuggestionStyle};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{AnonConst, Expr};
|
||||
|
@ -23,20 +18,21 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_parse::parser::ForceCollect;
|
||||
use rustc_resolve::rustdoc::{
|
||||
add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, DocFragment,
|
||||
};
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use rustc_span::{sym, BytePos, FileName, Pos, Span};
|
||||
use rustc_span::{sym, Span};
|
||||
use std::ops::Range;
|
||||
use std::{io, thread};
|
||||
use url::Url;
|
||||
|
||||
mod link_with_quotes;
|
||||
mod markdown;
|
||||
mod missing_headers;
|
||||
mod needless_doctest_main;
|
||||
mod suspicious_doc_comments;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the presence of `_`, `::` or camel-case words
|
||||
|
@ -203,6 +199,39 @@ declare_clippy_lint! {
|
|||
"presence of `fn main() {` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[test]` in doctests unless they are marked with
|
||||
/// either `ignore`, `no_run` or `compile_fail`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Code in examples marked as `#[test]` will somewhat
|
||||
/// surprisingly not be run by `cargo test`. If you really want
|
||||
/// to show how to test stuff in an example, mark it `no_run` to
|
||||
/// make the intent clear.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// /// An example of a doctest with a `main()` function
|
||||
/// ///
|
||||
/// /// # Examples
|
||||
/// ///
|
||||
/// /// ```
|
||||
/// /// #[test]
|
||||
/// /// fn equality_works() {
|
||||
/// /// assert_eq!(1_u8, 1);
|
||||
/// /// }
|
||||
/// /// ```
|
||||
/// fn test_attr_in_doctest() {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub TEST_ATTR_IN_DOCTEST,
|
||||
suspicious,
|
||||
"presence of `#[test]` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
|
@ -309,34 +338,36 @@ declare_clippy_lint! {
|
|||
"suspicious usage of (outer) doc comments"
|
||||
}
|
||||
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
#[derive(Clone)]
|
||||
pub struct DocMarkdown {
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
in_trait_impl: bool,
|
||||
check_private_items: bool,
|
||||
}
|
||||
|
||||
impl DocMarkdown {
|
||||
pub fn new(valid_idents: &[String]) -> Self {
|
||||
impl Documentation {
|
||||
pub fn new(valid_idents: &[String], check_private_items: bool) -> Self {
|
||||
Self {
|
||||
valid_idents: valid_idents.iter().cloned().collect(),
|
||||
in_trait_impl: false,
|
||||
check_private_items,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DocMarkdown => [
|
||||
impl_lint_pass!(Documentation => [
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_MARKDOWN,
|
||||
MISSING_SAFETY_DOC,
|
||||
MISSING_ERRORS_DOC,
|
||||
MISSING_PANICS_DOC,
|
||||
NEEDLESS_DOCTEST_MAIN,
|
||||
TEST_ATTR_IN_DOCTEST,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
SUSPICIOUS_DOC_COMMENTS
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
||||
impl<'tcx> LateLintPass<'tcx> for Documentation {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
|
||||
check_attrs(cx, &self.valid_idents, attrs);
|
||||
|
@ -351,13 +382,17 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
|||
hir::ItemKind::Fn(ref sig, _, body_id) => {
|
||||
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let mut fpu = FindPanicUnwrap {
|
||||
|
||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
||||
missing_headers::check(
|
||||
cx,
|
||||
typeck_results: cx.tcx.typeck(item.owner_id.def_id),
|
||||
panic_span: None,
|
||||
};
|
||||
fpu.visit_expr(body.value);
|
||||
lint_for_missing_headers(cx, item.owner_id, sig, headers, Some(body_id), fpu.panic_span);
|
||||
item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
panic_span,
|
||||
self.check_private_items,
|
||||
);
|
||||
}
|
||||
},
|
||||
hir::ItemKind::Impl(impl_) => {
|
||||
|
@ -395,7 +430,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
|||
};
|
||||
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
|
||||
if !in_external_macro(cx.tcx.sess, item.span) {
|
||||
lint_for_missing_headers(cx, item.owner_id, sig, headers, None, None);
|
||||
missing_headers::check(cx, item.owner_id, sig, headers, None, None, self.check_private_items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -410,87 +445,16 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
|||
}
|
||||
if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let mut fpu = FindPanicUnwrap {
|
||||
cx,
|
||||
typeck_results: cx.tcx.typeck(item.owner_id.def_id),
|
||||
panic_span: None,
|
||||
};
|
||||
fpu.visit_expr(body.value);
|
||||
lint_for_missing_headers(cx, item.owner_id, sig, headers, Some(body_id), fpu.panic_span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_for_missing_headers(
|
||||
cx: &LateContext<'_>,
|
||||
owner_id: hir::OwnerId,
|
||||
sig: &hir::FnSig<'_>,
|
||||
headers: DocHeaders,
|
||||
body_id: Option<hir::BodyId>,
|
||||
panic_span: Option<Span>,
|
||||
) {
|
||||
if !cx.effective_visibilities.is_exported(owner_id.def_id) {
|
||||
return; // Private functions do not require doc comments
|
||||
}
|
||||
|
||||
// do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
|
||||
if cx
|
||||
.tcx
|
||||
.hir()
|
||||
.parent_iter(owner_id.into())
|
||||
.any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let span = cx.tcx.def_span(owner_id);
|
||||
match (headers.safety, sig.header.unsafety) {
|
||||
(false, hir::Unsafety::Unsafe) => span_lint(
|
||||
cx,
|
||||
MISSING_SAFETY_DOC,
|
||||
span,
|
||||
"unsafe function's docs miss `# Safety` section",
|
||||
),
|
||||
(true, hir::Unsafety::Normal) => span_lint(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
span,
|
||||
"safe function's docs have unnecessary `# Safety` section",
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
if !headers.panics && panic_span.is_some() {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
MISSING_PANICS_DOC,
|
||||
span,
|
||||
"docs for function which may panic missing `# Panics` section",
|
||||
panic_span,
|
||||
"first possible panic found here",
|
||||
);
|
||||
}
|
||||
if !headers.errors {
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, owner_id), sym::Result) {
|
||||
span_lint(
|
||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
||||
missing_headers::check(
|
||||
cx,
|
||||
MISSING_ERRORS_DOC,
|
||||
span,
|
||||
"docs for function returning `Result` missing `# Errors` section",
|
||||
);
|
||||
} else if let Some(body_id) = body_id
|
||||
&& let Some(future) = cx.tcx.lang_items().future_trait()
|
||||
&& let typeck = cx.tcx.typeck_body(body_id)
|
||||
&& let body = cx.tcx.hir().body(body_id)
|
||||
&& let ret_ty = typeck.expr_ty(body.value)
|
||||
&& implements_trait(cx, ret_ty, future, &[])
|
||||
&& let ty::Coroutine(_, subs, _) = ret_ty.kind()
|
||||
&& is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MISSING_ERRORS_DOC,
|
||||
span,
|
||||
"docs for function returning `Result` missing `# Errors` section",
|
||||
item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
panic_span,
|
||||
self.check_private_items,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -515,6 +479,13 @@ struct DocHeaders {
|
|||
panics: bool,
|
||||
}
|
||||
|
||||
/// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and
|
||||
/// then delegates to `check_doc`.
|
||||
/// Some lints are already checked here if they can work with attributes directly and don't need
|
||||
/// to work with markdown.
|
||||
/// Others are checked elsewhere, e.g. in `check_doc` if they need access to markdown, or
|
||||
/// back in the various late lint pass methods if they need the final doc headers, like "Safety" or
|
||||
/// "Panics" sections.
|
||||
fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
|
||||
/// We don't want the parser to choke on intra doc links. Since we don't
|
||||
/// actually care about rendering them, just pretend that all broken links
|
||||
|
@ -528,7 +499,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
|||
return None;
|
||||
}
|
||||
|
||||
check_almost_inner_doc(cx, attrs);
|
||||
suspicious_doc_comments::check(cx, attrs);
|
||||
|
||||
let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
|
||||
let mut doc = String::new();
|
||||
|
@ -558,45 +529,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
|||
))
|
||||
}
|
||||
|
||||
/// Looks for `///!` and `/**!` comments, which were probably meant to be `//!` and `/*!`
|
||||
fn check_almost_inner_doc(cx: &LateContext<'_>, attrs: &[Attribute]) {
|
||||
let replacements: Vec<_> = attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if let AttrKind::DocComment(com_kind, sym) = attr.kind
|
||||
&& let AttrStyle::Outer = attr.style
|
||||
&& let Some(com) = sym.as_str().strip_prefix('!')
|
||||
{
|
||||
let sugg = match com_kind {
|
||||
CommentKind::Line => format!("//!{com}"),
|
||||
CommentKind::Block => format!("/*!{com}*/"),
|
||||
};
|
||||
Some((attr.span, sugg))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
lo_span.to(hi_span),
|
||||
"this is an outer doc comment and does not apply to the parent module or crate",
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"use an inner doc comment to document the parent module or crate",
|
||||
replacements,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
|
||||
|
||||
/// Checks parsed documentation.
|
||||
/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,
|
||||
/// so lints here will generally access that information.
|
||||
/// Returns documentation headers -- whether a "Safety", "Errors", "Panic" section was found
|
||||
#[allow(clippy::too_many_lines)] // Only a big match statement
|
||||
fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
|
||||
cx: &LateContext<'_>,
|
||||
|
@ -611,6 +549,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
let mut in_heading = false;
|
||||
let mut is_rust = false;
|
||||
let mut no_test = false;
|
||||
let mut ignore = false;
|
||||
let mut edition = None;
|
||||
let mut ticks_unbalanced = false;
|
||||
let mut text_to_check: Vec<(CowStr<'_>, Range<usize>)> = Vec::new();
|
||||
|
@ -626,6 +565,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
break;
|
||||
} else if item == "no_test" {
|
||||
no_test = true;
|
||||
} else if item == "no_run" || item == "compile_fail" {
|
||||
ignore = true;
|
||||
}
|
||||
if let Some(stripped) = item.strip_prefix("edition") {
|
||||
is_rust = true;
|
||||
|
@ -639,6 +580,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
End(CodeBlock(_)) => {
|
||||
in_code = false;
|
||||
is_rust = false;
|
||||
ignore = false;
|
||||
},
|
||||
Start(Link(_, url, _)) => in_link = Some(url),
|
||||
End(Link(..)) => in_link = None,
|
||||
|
@ -665,7 +607,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
} else {
|
||||
for (text, range) in text_to_check {
|
||||
if let Some(span) = fragments.span(cx, range) {
|
||||
check_text(cx, valid_idents, &text, span);
|
||||
markdown::check(cx, valid_idents, &text, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -692,11 +634,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
if in_code {
|
||||
if is_rust && !no_test {
|
||||
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
|
||||
check_code(cx, &text, edition, range.clone(), fragments);
|
||||
needless_doctest_main::check(cx, &text, edition, range.clone(), fragments, ignore);
|
||||
}
|
||||
} else {
|
||||
if in_link.is_some() {
|
||||
check_link_quotes(cx, trimmed_text, range.clone(), fragments);
|
||||
link_with_quotes::check(cx, trimmed_text, range.clone(), fragments);
|
||||
}
|
||||
if let Some(link) = in_link.as_ref()
|
||||
&& let Ok(url) = Url::parse(link)
|
||||
|
@ -713,208 +655,28 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
headers
|
||||
}
|
||||
|
||||
fn check_link_quotes(cx: &LateContext<'_>, trimmed_text: &str, range: Range<usize>, fragments: Fragments<'_>) {
|
||||
if trimmed_text.starts_with('\'')
|
||||
&& trimmed_text.ends_with('\'')
|
||||
&& let Some(span) = fragments.span(cx, range)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
span,
|
||||
"possible intra-doc link using quotes instead of backticks",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<usize>, fragments: Fragments<'_>) {
|
||||
fn has_needless_main(code: String, edition: Edition) -> bool {
|
||||
rustc_driver::catch_fatal_errors(|| {
|
||||
rustc_span::create_session_globals_then(edition, || {
|
||||
let filename = FileName::anon_source_code(&code);
|
||||
|
||||
let fallback_bundle =
|
||||
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
|
||||
let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
|
||||
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
#[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
|
||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
drop(errs);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
let mut relevant_main_found = false;
|
||||
loop {
|
||||
match parser.parse_item(ForceCollect::No) {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
ItemKind::Fn(box Fn {
|
||||
sig, body: Some(block), ..
|
||||
}) if item.ident.name == sym::main => {
|
||||
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
|
||||
let returns_nothing = match &sig.decl.output {
|
||||
FnRetTy::Default(..) => true,
|
||||
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
||||
FnRetTy::Ty(_) => false,
|
||||
};
|
||||
|
||||
if returns_nothing && !is_async && !block.stmts.is_empty() {
|
||||
// This main function should be linted, but only if there are no other functions
|
||||
relevant_main_found = true;
|
||||
} else {
|
||||
// This main function should not be linted, we're done
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..)
|
||||
// Another function was found; this case is ignored
|
||||
| ItemKind::Fn(..) => return false,
|
||||
_ => {},
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
e.cancel();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
relevant_main_found
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
let trailing_whitespace = text.len() - text.trim_end().len();
|
||||
|
||||
// Because of the global session, we need to create a new session in a different thread with
|
||||
// the edition we need.
|
||||
let text = text.to_owned();
|
||||
if thread::spawn(move || has_needless_main(text, edition))
|
||||
.join()
|
||||
.expect("thread::spawn failed")
|
||||
&& let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace)
|
||||
{
|
||||
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
|
||||
for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
|
||||
// Trim punctuation as in `some comment (see foo::bar).`
|
||||
// ^^
|
||||
// Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
|
||||
let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
|
||||
|
||||
// Remove leading or trailing single `:` which may be part of a sentence.
|
||||
if word.starts_with(':') && !word.starts_with("::") {
|
||||
word = word.trim_start_matches(':');
|
||||
}
|
||||
if word.ends_with(':') && !word.ends_with("::") {
|
||||
word = word.trim_end_matches(':');
|
||||
}
|
||||
|
||||
if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust for the current word
|
||||
let offset = word.as_ptr() as usize - text.as_ptr() as usize;
|
||||
let span = Span::new(
|
||||
span.lo() + BytePos::from_usize(offset),
|
||||
span.lo() + BytePos::from_usize(offset + word.len()),
|
||||
span.ctxt(),
|
||||
span.parent(),
|
||||
);
|
||||
|
||||
check_word(cx, word, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
|
||||
/// Checks if a string is upper-camel-case, i.e., starts with an uppercase and
|
||||
/// contains at least two uppercase letters (`Clippy` is ok) and one lower-case
|
||||
/// letter (`NASA` is ok).
|
||||
/// Plurals are also excluded (`IDs` is ok).
|
||||
fn is_camel_case(s: &str) -> bool {
|
||||
if s.starts_with(|c: char| c.is_ascii_digit() | c.is_ascii_lowercase()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let s = s.strip_suffix('s').unwrap_or(s);
|
||||
|
||||
s.chars().all(char::is_alphanumeric)
|
||||
&& s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
|
||||
&& s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
|
||||
}
|
||||
|
||||
fn has_underscore(s: &str) -> bool {
|
||||
s != "_" && !s.contains("\\_") && s.contains('_')
|
||||
}
|
||||
|
||||
fn has_hyphen(s: &str) -> bool {
|
||||
s != "-" && s.contains('-')
|
||||
}
|
||||
|
||||
if let Ok(url) = Url::parse(word) {
|
||||
// try to get around the fact that `foo::bar` parses as a valid URL
|
||||
if !url.cannot_be_a_base() {
|
||||
span_lint(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
|
||||
if has_underscore(word) && has_hyphen(word) {
|
||||
return;
|
||||
}
|
||||
|
||||
if has_underscore(word) || word.contains("::") || is_camel_case(word) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"item in documentation is missing backticks",
|
||||
|diag| {
|
||||
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||
diag.span_suggestion_with_style(
|
||||
span,
|
||||
"try",
|
||||
format!("`{snippet}`"),
|
||||
applicability,
|
||||
// always show the suggestion in a separate line, since the
|
||||
// inline presentation adds another pair of backticks
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct FindPanicUnwrap<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
panic_span: Option<Span>,
|
||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
|
||||
pub fn find_span(
|
||||
cx: &'a LateContext<'tcx>,
|
||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
body: impl Visitable<'tcx>,
|
||||
) -> Option<Span> {
|
||||
let mut vis = Self {
|
||||
cx,
|
||||
panic_span: None,
|
||||
typeck_results,
|
||||
};
|
||||
body.visit(&mut vis);
|
||||
vis.panic_span
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
135
clippy_lints/src/doc/needless_doctest_main.rs
Normal file
135
clippy_lints/src/doc/needless_doctest_main.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use std::ops::Range;
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::{Async, Fn, FnRetTy, Item, ItemKind};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::emitter::EmitterWriter;
|
||||
use rustc_errors::Handler;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_parse::parser::ForceCollect;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use rustc_span::{sym, FileName, Pos};
|
||||
|
||||
use super::Fragments;
|
||||
|
||||
fn get_test_spans(item: &Item, test_attr_spans: &mut Vec<Range<usize>>) {
|
||||
test_attr_spans.extend(
|
||||
item.attrs
|
||||
.iter()
|
||||
.find(|attr| attr.has_name(sym::test))
|
||||
.map(|attr| attr.span.lo().to_usize()..item.ident.span.hi().to_usize()),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
text: &str,
|
||||
edition: Edition,
|
||||
range: Range<usize>,
|
||||
fragments: Fragments<'_>,
|
||||
ignore: bool,
|
||||
) {
|
||||
// return whether the code contains a needless `fn main` plus a vector of byte position ranges
|
||||
// of all `#[test]` attributes in not ignored code examples
|
||||
fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec<Range<usize>>) {
|
||||
rustc_driver::catch_fatal_errors(|| {
|
||||
rustc_span::create_session_globals_then(edition, || {
|
||||
let mut test_attr_spans = vec![];
|
||||
let filename = FileName::anon_source_code(&code);
|
||||
|
||||
let fallback_bundle =
|
||||
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
|
||||
let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
|
||||
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
#[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
|
||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
drop(errs);
|
||||
return (false, test_attr_spans);
|
||||
},
|
||||
};
|
||||
|
||||
let mut relevant_main_found = false;
|
||||
let mut eligible = true;
|
||||
loop {
|
||||
match parser.parse_item(ForceCollect::No) {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
ItemKind::Fn(box Fn {
|
||||
sig, body: Some(block), ..
|
||||
}) if item.ident.name == sym::main => {
|
||||
if !ignore {
|
||||
get_test_spans(&item, &mut test_attr_spans);
|
||||
}
|
||||
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
|
||||
let returns_nothing = match &sig.decl.output {
|
||||
FnRetTy::Default(..) => true,
|
||||
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
||||
FnRetTy::Ty(_) => false,
|
||||
};
|
||||
|
||||
if returns_nothing && !is_async && !block.stmts.is_empty() {
|
||||
// This main function should be linted, but only if there are no other functions
|
||||
relevant_main_found = true;
|
||||
} else {
|
||||
// This main function should not be linted, we're done
|
||||
eligible = false;
|
||||
}
|
||||
},
|
||||
// Another function was found; this case is ignored for needless_doctest_main
|
||||
ItemKind::Fn(box Fn { .. }) => {
|
||||
eligible = false;
|
||||
if !ignore {
|
||||
get_test_spans(&item, &mut test_attr_spans);
|
||||
}
|
||||
},
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..) => {
|
||||
eligible = false;
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
e.cancel();
|
||||
return (false, test_attr_spans);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
(relevant_main_found & eligible, test_attr_spans)
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
let trailing_whitespace = text.len() - text.trim_end().len();
|
||||
|
||||
// Because of the global session, we need to create a new session in a different thread with
|
||||
// the edition we need.
|
||||
let text = text.to_owned();
|
||||
let (has_main, test_attr_spans) = thread::spawn(move || check_code_sample(text, edition, ignore))
|
||||
.join()
|
||||
.expect("thread::spawn failed");
|
||||
if has_main && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) {
|
||||
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
|
||||
}
|
||||
for span in test_attr_spans {
|
||||
let span = (range.start + span.start)..(range.start + span.end);
|
||||
if let Some(span) = fragments.span(cx, span) {
|
||||
span_lint(cx, TEST_ATTR_IN_DOCTEST, span, "unit tests in doctest are not executed");
|
||||
}
|
||||
}
|
||||
}
|
48
clippy_lints/src/doc/suspicious_doc_comments.rs
Normal file
48
clippy_lints/src/doc/suspicious_doc_comments.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::SUSPICIOUS_DOC_COMMENTS;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
|
||||
let replacements: Vec<_> = collect_doc_replacements(attrs);
|
||||
|
||||
if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
lo_span.to(hi_span),
|
||||
"this is an outer doc comment and does not apply to the parent module or crate",
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"use an inner doc comment to document the parent module or crate",
|
||||
replacements,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
|
||||
attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if let AttrKind::DocComment(com_kind, sym) = attr.kind
|
||||
&& let AttrStyle::Outer = attr.style
|
||||
&& let Some(com) = sym.as_str().strip_prefix('!')
|
||||
{
|
||||
let sugg = match com_kind {
|
||||
CommentKind::Line => format!("//!{com}"),
|
||||
CommentKind::Block => format!("/*!{com}*/"),
|
||||
};
|
||||
Some((attr.span, sugg))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
|
|||
use clippy_utils::{get_parent_node, is_must_use_func_call};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
|||
use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{FileName, Span};
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
|||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::peel_blocks;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -4,7 +4,7 @@ use rustc_ast::ast::{Item, ItemKind, VariantData};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_lexer::TokenKind;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hir::{Expr, ExprKind};
|
|||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Symbol;
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_hir::hir_id::HirIdSet;
|
|||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_hir::{Item, ItemKind};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, IntTy, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_hir::{Expr, ExprKind, Pat, PatKind};
|
|||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -46,9 +46,12 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
|
|||
pats.iter().all(unary_pattern)
|
||||
}
|
||||
match &pat.kind {
|
||||
PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Never | PatKind::Or(_) => {
|
||||
false
|
||||
},
|
||||
PatKind::Slice(_, _, _)
|
||||
| PatKind::Range(_, _, _)
|
||||
| PatKind::Binding(..)
|
||||
| PatKind::Wild
|
||||
| PatKind::Never
|
||||
| PatKind::Or(_) => false,
|
||||
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
|
||||
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
|
||||
PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
|||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Visibility;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::Span;
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_middle::ty::{
|
|||
self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
|
||||
ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults,
|
||||
};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||
|
@ -220,7 +220,8 @@ fn check_inputs(
|
|||
params.len() == self_arg.map_or(0, |_| 1) + args.len()
|
||||
&& params.iter().zip(self_arg.into_iter().chain(args)).all(|(p, arg)| {
|
||||
matches!(
|
||||
p.pat.kind,PatKind::Binding(BindingAnnotation::NONE, id, _, None)
|
||||
p.pat.kind,
|
||||
PatKind::Binding(BindingAnnotation::NONE, id, _, None)
|
||||
if path_to_local_id(arg, id)
|
||||
)
|
||||
// Only allow adjustments which change regions (i.e. re-borrowing).
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool};
|
|||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_ast::visit::{walk_block, walk_item, Visitor};
|
|||
use rustc_ast::{Block, Crate, Inline, Item, ItemKind, ModKind, NodeId};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::source::indent_of;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint;
|
|||
use clippy_utils::is_entrypoint_fn;
|
||||
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, ExpnId};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_hir::{
|
|||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
|||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, FloatTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use std::fmt;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use rustc_ast::ast;
|
||||
|
@ -496,9 +496,13 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
if let BinOpKind::Sub = op { -sugg } else { sugg }
|
||||
};
|
||||
|
||||
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
|
||||
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs)
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
{
|
||||
(inner_lhs, Sugg::hir(cx, inner_rhs, ".."), maybe_neg_sugg(rhs))
|
||||
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
|
||||
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs)
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
{
|
||||
(inner_lhs, maybe_neg_sugg(inner_rhs), Sugg::hir(cx, lhs, ".."))
|
||||
} else {
|
||||
return;
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -19,7 +19,7 @@ use rustc_hir::{Expr, ExprKind, LangItem};
|
|||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::edition::Edition::Edition2021;
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_ast::{FormatArgsPiece, FormatTrait};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_lang_item;
|
|||
use clippy_utils::{higher, match_def_path, paths};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt;
|
|||
use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Item;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -12,7 +12,7 @@ use rustc_hir::{
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{RawPtr, TypeAndMut};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -9,7 +9,7 @@ mod too_many_lines;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hir::{Body, FnDecl};
|
|||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_errors::Diagnostic;
|
|||
use rustc_hir::intravisit::{self as visit, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
|||
use clippy_utils::is_else_clause;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_hir::LangItem::{OptionNone, OptionSome};
|
|||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -3,7 +3,7 @@ use hir::{Node, PatKind};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
106
clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
Normal file
106
clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Item, ItemKind, Path, TraitRef};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// This lint is concerned with the semantics of `Borrow` and `Hash` for a
|
||||
/// type that implements all three of `Hash`, `Borrow<str>` and `Borrow<[u8]>`
|
||||
/// as it is impossible to satisfy the semantics of Borrow and `Hash` for
|
||||
/// both `Borrow<str>` and `Borrow<[u8]>`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// When providing implementations for `Borrow<T>`, one should consider whether the different
|
||||
/// implementations should act as facets or representations of the underlying type. Generic code
|
||||
/// typically uses `Borrow<T>` when it relies on the identical behavior of these additional trait
|
||||
/// implementations. These traits will likely appear as additional trait bounds.
|
||||
///
|
||||
/// In particular `Eq`, `Ord` and `Hash` must be equivalent for borrowed and owned values:
|
||||
/// `x.borrow() == y.borrow()` should give the same result as `x == y`.
|
||||
/// It follows then that the following equivalence must hold:
|
||||
/// `hash(x) == hash((x as Borrow<[u8]>).borrow()) == hash((x as Borrow<str>).borrow())`
|
||||
///
|
||||
/// Unfortunately it doesn't hold as `hash("abc") != hash("abc".as_bytes())`.
|
||||
/// This happens because the `Hash` impl for str passes an additional `0xFF` byte to
|
||||
/// the hasher to avoid collisions. For example, given the tuples `("a", "bc")`, and `("ab", "c")`,
|
||||
/// the two tuples would have the same hash value if the `0xFF` byte was not added.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::borrow::Borrow;
|
||||
/// use std::hash::{Hash, Hasher};
|
||||
///
|
||||
/// struct ExampleType {
|
||||
/// data: String
|
||||
/// }
|
||||
///
|
||||
/// impl Hash for ExampleType {
|
||||
/// fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
/// self.data.hash(state);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Borrow<str> for ExampleType {
|
||||
/// fn borrow(&self) -> &str {
|
||||
/// &self.data
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Borrow<[u8]> for ExampleType {
|
||||
/// fn borrow(&self) -> &[u8] {
|
||||
/// self.data.as_bytes()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// As a consequence, hashing a `&ExampleType` and hashing the result of the two
|
||||
/// borrows will result in different values.
|
||||
///
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub IMPL_HASH_BORROW_WITH_STR_AND_BYTES,
|
||||
correctness,
|
||||
"ensures that the semantics of `Borrow` for `Hash` are satisfied when `Borrow<str>` and `Borrow<[u8]>` are implemented"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImplHashWithBorrowStrBytes => [IMPL_HASH_BORROW_WITH_STR_AND_BYTES]);
|
||||
|
||||
impl LateLintPass<'_> for ImplHashWithBorrowStrBytes {
|
||||
/// We are emitting this lint at the Hash impl of a type that implements all
|
||||
/// three of `Hash`, `Borrow<str>` and `Borrow<[u8]>`.
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if let ItemKind::Impl(imp) = item.kind
|
||||
&& let Some(TraitRef {path: Path {span, res, ..}, ..}) = imp.of_trait
|
||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||
&& let Some(hash_id) = cx.tcx.get_diagnostic_item(sym::Hash)
|
||||
&& Res::Def(DefKind::Trait, hash_id) == *res
|
||||
&& let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow)
|
||||
// since we are in the `Hash` impl, we don't need to check for that.
|
||||
// we need only to check for `Borrow<str>` and `Borrow<[u8]>`
|
||||
&& implements_trait(cx, ty, borrow_id, &[cx.tcx.types.str_.into()])
|
||||
&& implements_trait(cx, ty, borrow_id, &[Ty::new_slice(cx.tcx, cx.tcx.types.u8).into()])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IMPL_HASH_BORROW_WITH_STR_AND_BYTES,
|
||||
*span,
|
||||
"the semantics of `Borrow<T>` around `Hash` can't be satisfied when both `Borrow<str>` and `Borrow<[u8]>` are implemented",
|
||||
|diag| {
|
||||
diag.note("the `Borrow` semantics require that `Hash` must behave the same for all implementations of Borrow<T>");
|
||||
diag.note(
|
||||
"however, the hash implementations of a string (`str`) and the bytes of a string `[u8]` do not behave the same ..."
|
||||
);
|
||||
diag.note("... as (`hash(\"abc\") != hash(\"abc\".as_bytes())`");
|
||||
diag.help("consider either removing one of the `Borrow` implementations (`Borrow<str>` or `Borrow<[u8]>`) ...");
|
||||
diag.help("... or not implementing `Hash` for this type");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ use rustc_hir_analysis::hir_ty_to_ty;
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{Ty, TypeckResults};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_hir::intravisit::FnKind;
|
|||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{Int, IntTy, Ty, Uint, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue